是否有一种方法可以为.gitignore使用符号链接?为什么它不再以符号链接而受到支持?

发布于 2025-02-10 09:18:36 字数 402 浏览 1 评论 0 原文

我的笔记本电脑上有一个中央 .gitignore ,对于我创建的每个项目,我都会为该中心文件创建一个符号链接,以便我可以在所有项目中保留统一的策略。

我所有的项目都彼此(从技术方面)彼此相同,并且拥有一个中央 .gitignore 以减轻维护负担是很有意义的。

但是,最近我看到了此消息:

警告:无法访问'.gitignore':太多级别的符号链接

,当我搜索时,似乎从GIT 2.3上方,他们决定不支持符号链接。

我有两个问题。首先,有没有办法强迫git支持 .gitignore 的符号链接?为什么他们不再支持它?重复使用代码没有意义吗? Linux的一半是否未通过符号链接重新使用?

I have a central .gitignore on my laptop and for each project that I create, I create a symbolic link to that central file so that I can keep a uniform policy across all of my projects.

All of my projects are like each other (technology-wise) and it makes sense to have a central .gitignore to reduce the burden of maintenance.

However, recently I see this message:

warning: unable to access '.gitignore': Too many levels of symbolic links

And as I searched, it seems that from git 2.3 upwards they have decided to not support the symbolic link.

I have two questions. First, is there a way to force git to support symbolic links for .gitignore? And why on Earth do they not support it anymore? Does it not make sense to reuse code? Is half of linux not reused through symbolic links?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

只为一人 2025-02-17 09:18:37

首先,有没有办法强迫git支持 .gitignore

的符号链接

不。

为什么他们在地球上不再支持它?

gitattributes文档现在(截至git 2.32起)说这是末尾:

注释

git在工作树中访问 .gitattributes 文件时不会遵循符号链接。从索引或从文件系统中访问了文件时,这会使行为保持一致。

虽然我本人并不是100%卖出的推理,但确实有意义。 (在我看来,Git可以将 .gitattributes 文件的内容填充到索引中,从而将其填充到提交中,尽管这意味着在 Checkout 上,它将销毁符号链接。)

可选的进一步阅读 /背景

,让我们首先描述一个“符号链接” 是< / em>。要做到这一点实体,通常在A 文件系统(文件的系统收集)中找到,该数据存储数据以进行以后的检索。作为一个命名实体,文件具有一个名称:例如, readme.txt makefile .gitConfig 都是 file名称。不同的OS在文件名上放置不同的约束(例如,Windows拒绝将COLON 存储在文件名中,或创建任何名为 aux 使用或不带有后缀的文件,所以您不能拥有c或c ++包括名为 aux.h aux.hpp 的文件。 git本身对文件名的限制很少:它们几乎可以包含除了ascii nul 以外的任何字符( b'\ 0'在python中, \ 0 在C等中)和Forward Slashes /有点特别,但是除此之外,名称字符只是一个名称字符,很少有限制。 1

在大多数实际OS上,文件可以具有“类型”。这里的确切机制迅速变得特定于OS,并且可能变得非常复杂, 2 ,尽管传统的Unix样层次结构文件系统只有几种类型:“目录”,“文件”,“块或字符设备” “,”符号链接”等。符号链接实际上是其中一种。

符号链接是一种文件,其中文件的 content 是另一个文件名。此文件名,在类似于Unix的文件系统上,可以是绝对/home/john/somefile /users/torek/somefile )或相对 ./ somefile ../../ somefile )。在这些系统上,打开符号链接会导致打开符号链接内容提供名称的文件。 读取符号链接的内容 - 也就是说,找出文件 name link包含 - 我们使用其他操作:<代码> readlink 而不是开放。现代Unix系统还具有 o_nofollow 标志,可用于禁止 open 从下面的链接中调用。 3

git >商店 symlink作为特殊模式在提交中的对象:普通文件是模式100644 ,意思是不可启用的文件,或者> 模式100755 ,意思是可执行文件。符号链接存储为模式120000 和git存储目标名称,通过调用 readlink ,作为内容


。 一个特殊的限制是,您不允许将任何名为 .git 的东西存储在任何混合物或上部和/或下部情况下。此 .git 限制实际上适用于“名称组件”,后者是前向斜线之间的部分。由于Windows是Windows,因此windows将在必要时将向后的斜线变成向前的斜线,然后将限制放在组件上。

例如, 2 从1960年代到1980年代的传统OS可能会施加基于文件类型的部分访问方法。 Unix在这里简化了很多东西。

3 这对于各个安全方面有时很重要。细节超出了本文的范围。

4 这些奇数模式值与 struct stat st_mode 字段 系统调用。那是因为当Linus Torvalds首次编写了Git的初始版本时,他将其作为一种文件系统来处理。保留了存储完整Unix文件模式(9位 rwxrwxrwx flags)的能力,最初是GIT实际存储的Group Write Permissions,但事实证明这是一个错误,并在第一个公众之前被删除了发布。 100000 零件是 s_ifreg ,“ stat:inode格式常规文件”。 120000 在GIT符号链接中找到的是 s_iflnk 或“ stat:inode格式格式符号链接”。我们还具有模式040000 针对目录的 s_ifdir ,现在应该很明显。但是,git不能将模式040000 输入其 index / staging-area ,这并不是特别好的理由,这导致了我如何将空白目录添加到git存储库中?


换句话说,符号链接表示“使用另一个”文件“

在找到符号链接的任何地方,都意味着读取或编写其他文件。因此,如果 readme.txt 是符号链接读取/tmp/doomedyou 实际读取/tmp/doomedyou 而读取;任何尝试 readme.txt 的尝试实际上都会写入/tmp/doomedyou

但是,考虑到 此重定向 - 从 readme.txt /tmp/doomedyou - occurs。当您建立符号链接本身时,这不会发生。您可以创建此 readme.txt file 去年。当我转到读取 readme.txt 那是重定向发生的时候。因此,如果您更改了/tmp/doomedyou ,则由于您创建了 readme.txt ,因此我获得了 Modern 版本,而不是旧版本。

当然,这正是您首先想要符号链接的原因:

我所有的项目都彼此(技术方面),并且拥有一个中央 .gitignore 减轻维护负担是很有意义的。

换句话说,您想拥有一个 .gitignore ,即不是版本的控制,总是会根据您的内容反映出应该忽略的内容直到现在就学会了,无论何时“现在”。

这与Git的正常目的相反,即在当时的“回到当时”,即“当时的“回到”是:您制作的对立面。 > git commit 快照。

我建议的可能性是,当您运行时:

git add .gitignore

要更新Git在 .gitignore 下一个conters 中应采取的措施。 > .gitignore 间接当时,读取符号链接的目标内容 ,然后准备要承诺的内容。然后,您将进行订单 - 快照和元数据 - 如果明年您提取此特定的历史提交,您将获得历史快照,包括历史 .gitignore

这样的缺点是,通过提取历史 .gitignore ,您“破坏链接”: .gitignore 不再是符号链接全部。相反,它是一个普通文件,包含历史快照。除了删除普通文件并创建一个新的符号链接外,无法获得链接 back

在GIT版本2.32之前,Git会注意到 .gitignore 是符号链接,并将存储在其索引/登台区域中,这一事实是 .giginore 是Symlink( 模式120000 ),并使用 readlink 系统调用来查找符号链接的目标,然后将其存储在提交中。然后运行 git Commit 然后制作一个快照,提取时,创建 .gitignore as a(new)符号链接:现有的file-or--删除Symlink,然后安装新的链接。它以通常的符号链接方式将其重定向到保存(致力于)的历史位置 - 即使这是错误的

从GIT版本2.32开始,GIT仍将 Store 符号链接 .gitignore file:

$ mkdir z; cd z
$ ../git --version
git version 2.36.1.363.g9c897eef06
$ ../git init
[messages snipped; branch renamed to main, also snipped]
$ echo testing > README
$ ln -s foo .gitignore
$ git add README .gitignore
$ git commit -m initial
[main (root-commit) 08c6626] initial
 2 files changed, 2 insertions(+)
 create mode 120000 .gitignore
 create mode 100644 README
$ ../git ls-tree HEAD
120000 blob 19102815663d23f8b75a47e7a01965dcdc96468c    .gitignore
100644 blob 038d718da6a1ebbc6a7780a96ed75a70cc2ad6e2    README

相同的推理 - 一旦将其制成并塞入存储库,则该comport consits consip 可能包含一个不再有效或不正确的符号链接 - 解释为什么git 2.32 也拒绝遵循 .gitattributes .mailmap files。请注意, git Archive 之类的命令通常使用 commit的版本 .gitattributes 控制存档替换,因此符号链接存储了 in < /em>存储库是没有用的,除非符号链接的目标在某种程度上是正确的。存储库及其提交从一台计算机上运到另一台机器上,但是在许多情况下,任何承诺的符号链接的目标

First, is there a way to force git to support symbolic links for .gitignore?

No.

And why on Earth do they not support it anymore?

The gitattributes documentation now (as of Git 2.32) says this near the end:

NOTES

Git does not follow symbolic links when accessing a .gitattributes file in the working tree. This keeps behavior consistent when the file is accessed from the index or a tree versus from the filesystem.

While I'm not 100% sold on the reasoning here myself, it does make sense. (It seems to me that Git could just stuff the content of the .gitattributes file into the index and hence into the commits, although this would mean that on checkout it would destroy the symlink.)

Optional further reading / background

First, let's describe what a "symbolic link" is in the first place. To do this we must define what a file is (which is a pretty big job, so we'll just do very light bit of coverage): A file is a named entity, typically found in a file system (systematic collection of files), that store data for later retrieval. Being a named entity, a file has a name: for instance, README.txt, Makefile, and .gitconfig are all file names. Different OSes place different constraints on file names (e.g., Windows refuses to store a colon : character in a file name or create any file named aux with or without a suffix, so that you cannot have a C or C++ include file named aux.h or aux.hpp). Git itself places very few constraints on file names: they can contain almost any character except an ASCII NUL (b'\0' in Python, \0 in C, etc.), and forward slashes / are slightly special, but other than that a name character is just a name character and there are very few restrictions.1

On most real OSes, files can have "types". The exact mechanisms here rapidly become OS-specific and can get very complicated,2 though traditional Unix-like hierarchical file systems just have a few types: "directory", "file", "block or character device", "symbolic link", and the like. Symbolic links are in fact one of these types.

A symbolic link is a type of file in which the file's content is another file name. This file name, on a Unix-like file system, can be absolute (/home/john/somefile, /Users/torek/somefile) or relative (./somefile, ../../somefile). On these systems, opening a symbolic link results in opening the file whose name is provided by the symbolic link's content. To read the content of the symbolic link—that is, to find out what file name the link contains—we use a different operation: readlink instead of open, for instance. Modern Unix systems also have an O_NOFOLLOW flag that can be used to forbid the open system call from following the link.3

The way Git stores a symlink is as a special mode object in a commit: ordinary files are either mode 100644, meaning a non-executable file, or mode 100755, meaning an executable file. A symbolic link is stored as mode 120000 and Git stores the target name, found by calling readlink, as the content.4


1The one peculiar restriction is that you're not allowed to store anything named .git, in any mix or upper and/or lower case. This .git restriction actually applies to "name components" which are the parts between forward slashes. Due to Windows being Windows, Git-on-Windows will turn backwards slashes into forwards ones as necessary, and then places the restriction on the components.

2Traditional OSes from the 1960s through 1980s, for instance, may impose things called access methods based in part on file types. Unix simplified things a lot here.

3This is sometimes important for various security aspects. The details are beyond the scope of this article.

4These odd mode values correspond closely to the struct stat st_mode field in a Unix/Linux stat system call. That's because when Linus Torvalds first wrote the initial versions of Git, he was dealing with it—at least in part—as a kind of file system. The ability to store full Unix file modes (9 bits of rwxrwxrwx flags) was left in, and initially Git actually stored group write permissions, but this turned out to be a mistake and was removed before the first public release. The 100000 part is S_IFREG, "Stat: Inode Format REGular file". The 120000 found in a Git symbolic link is S_IFLNK, or "Stat: Inode Format symbolic LiNK". We also have mode 040000 for directories from S_IFDIR, which should now be obvious. However, Git can't store a mode 040000 entry in its index / staging-area, for no particularly good reason, which leads to the problem described in How can I add a blank directory to a Git repository?


In other words, a symbolic link means "use another file"

Wherever a symbolic link is found, it means read or write some other file. So if README.txt is a symbolic link reading /tmp/fooledyou, any attempt to read README.txt actually reads /tmp/fooledyou instead; any attempt to write README.txt actually writes to /tmp/fooledyou.

Consider, though, when this redirection—from README.txt to /tmp/fooledyou—occurs. It doesn't happen at the time you make the symbolic link itself. You can create this README.txt file last year. When I go to read README.txt, that's when the redirection occurs. So if you've changed /tmp/fooledyou since you created README.txt, I get the modern version, not the old one.

That, of course, is precisely why you wanted the symbolic link in the first place:

All of my projects are like each other (technology-wise) and it makes sense to have a central .gitignore to reduce the burden of maintenance.

In other words, you wanted to have one .gitignore, that is not version controlled, that always reflects what should be ignored based on what you learned up until right now, regardless of when it is that "right now" is.

This is the opposite of Git's normal purpose, which is to store a full snapshot of what your project looked like "back then", whenever "back then" was: the time at which you made a git commit snapshot.

My suggested possibility above is that when you run:

git add .gitignore

to update Git's idea of what should go in the .gitignore file that goes in the next commit, Git could follow the .gitignore indirection at that time, read the contents of the target of the symbolic link, and prepare that to be committed. You'd then make the commit—the snapshot and metadata—such that if, next year, you extract this particular historical commit, you'll get the historical snapshot, including the historical .gitignore.

The drawback to this is that by extracting the historical .gitignore, you "break the link": .gitignore is no longer a symbolic link at all. Instead, it is now an ordinary file, containing the historical snapshot. There's no way to get the link back except to remove the ordinary file and create a new symbolic link.

Before Git version 2.32, Git would notice when .gitignore was a symbolic link and would store, in its index / staging-area, the fact that .giginore was a symlink (mode 120000) and use the readlink system call to find the target of the symlink, and store that in the commit. Running git commit then makes a snapshot that, when extracted, creates .gitignore as a (new) symbolic link: the existing file-or-symlink is removed, and the new one is installed instead. It redirects, in the usual symlink fashion, to the saved (committed) historical location—even if that's wrong now.

As of Git version 2.32, Git will still store a symbolic link .gitignore file:

$ mkdir z; cd z
$ ../git --version
git version 2.36.1.363.g9c897eef06
$ ../git init
[messages snipped; branch renamed to main, also snipped]
$ echo testing > README
$ ln -s foo .gitignore
$ git add README .gitignore
$ git commit -m initial
[main (root-commit) 08c6626] initial
 2 files changed, 2 insertions(+)
 create mode 120000 .gitignore
 create mode 100644 README
$ ../git ls-tree HEAD
120000 blob 19102815663d23f8b75a47e7a01965dcdc96468c    .gitignore
100644 blob 038d718da6a1ebbc6a7780a96ed75a70cc2ad6e2    README

The same reasoning—that a Git commit, once it's made and stuffed into a repository, may contain a symbolic link that is no longer valid or correct—explains why Git 2.32 also refuses to follow .gitattributes and .mailmap files. Note that commands like git archive generally use the commit's version of .gitattributes to control archive substitutions, so a symbolic link stored in the repository is useless unless the target of the symbolic link is somehow correct. The repository and its commits get shipped around from one machine to another, but the targets of any committed symlinks in many cases don't.

伪心 2025-02-17 09:18:37

在Linux下,您始终有可能将.gitignore作为物理链接(#ln ...)而不是符号链接(#ln -s ...)。
这样,该链接被视为真实的物理文件。
这对我有用。

Under Linux, you always have the possibility to put .gitignore as a physical link (# ln ...) and not a symbolic link (# ln -s ...).
Like this, the link is seen as a real physical file.
This worked for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文