为什么我的 .gitattributes 文件没有阻止在 Windows 上检出文件时添加“\r"? [英] Why isn't my .gitattributes file preventing the addition of "\r"s when checking out files on Windows?

查看:30
本文介绍了为什么我的 .gitattributes 文件没有阻止在 Windows 上检出文件时添加“\r"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Windows 10 上的 Cygwin shell 中使用 Git 版本 2.28.0.windows.1.在我克隆我的存储库后,我可以看到这个

I'm using Git version 2.28.0.windows.1 within a Cygwin shell on Windows 10. After I do my clone of my repository, I can see this

$ cat .gitattributes 
* text=auto
*.sh text eol=lf

我设置它认为它会纠正错误的行尾(我想消除自动包含的\r"行尾).但是,在我进行克隆之后

I set this up thinking that it would correct bad line endings (I want to eliminate the "\r" line endings from getting automatically included). However, after I do my clone

git clone https://github.com/chicommons/maps.git
cd maps

我仍然可以看到我不想要的行尾...

I can still see the line endings I don't want ...

$ grep '\r' web/entrypoint.sh
python manage.py migrate
python manage.py migrate directory
python manage.py docker_init_db_data

我可以用我的.gitattributes"做什么?(或可能是另一个文件?)以防止出现这些行结尾?

What can I do with my ".gitattributes" (or possibly another file?) to prevent these line endings from appearing?

推荐答案

eol=lf 指令将阻止 Git 添加 回车,但不会阻止Git 保留现有回车.

The eol=lf directive will prevent Git from adding carriage returns, but it won't prevent Git from keeping existing carriage returns.

真正理解这里发生的事情需要一点关于 Git 如何在提交中存储文件的知识.关键是:

Truly understanding what's going on here requires a little bit of knowledge about how Git stores files inside commits. The keys to this are:

  • 每个提交都以只读、压缩、仅 Git 和去重格式存储每个文件的完整快照.这意味着您实际看到和处理/使用的文件不在存储库中:您使用的文件位于工作树工作树.

  • Each commit stores a full snapshot of every file, in a read-only, compressed, Git-only, and de-duplicated format. This means the files that you actually see and work on / with are not in the repository: the files you use are in your working tree or work-tree.

任何提交的所有部分,包括其所有文件,实际上都是不可更改的.如果你从存储库中取出一个 Git 内部对象(包括一个提交),以某种方式修改它,然后把它放回去,你没有改变原始对象;相反,你只是添加了另一个,这个新的得到一个不同的哈希ID.

All parts of any commit, including all of its files, are literally unchangeable. If you take a Git internal object (including a commit) out of the repository, modify it somehow, and put it back, you haven't changed the original; instead, you have just added another one, and this new one gets a different hash ID.

要将文件从提交中获取到工作树中,Git 必须将它们复制出来.这很明显;不明显的是还有第三个副本".每个文件.这第三个——或者说中间,真的——存在于 Git 所谓的索引,或者暂存区,或者——现在很少——缓存>.这三个名称均指同一个实体.

To get files from a commit into your work-tree, Git must copy them out. That much is obvious; what's not obvious is that there's a third "copy" of each file. This third—or middle, really—copy lives in what Git calls the index, or the staging area, or—rarely these days—the cache. All three names refer to the same entity.

也就是说,假设 HEAD 附加到分支名称 master 并且 master 当前代表一个提交,其哈希 ID 为 a123456....换句话说,这个带有丑陋的大哈希 ID 的提交是你的当前提交.在这个提交中,我们有名为 README.mdmain.py 的文件,以及——在你的例子中——web/migrate.sh.一共有三个副本"这个文件的.副本"这里用引号引起来,因为其中两个是自动去重的格式,所以实际上只有一个底层副本.

That is, suppose HEAD is attached to the branch name master and master currently represents a commit whose hash ID is a123456.... In other words, this commit—with its big ugly hash ID—is your current commit. Inside this commit, we have files named README.md and main.py and—in your case—web/migrate.sh. There are three "copies" of this file. "Copies" here is in quotes because two of them are in the automatically-de-duplicated format, so there is actually only one underlying copy.

我们可以在一张表中说明这三个副本,使用特殊名称HEAD来引用提交a123456...(当前提交):

We can illustrate these three copies in a table, using the special name HEAD to refer to commit a123456... (the current commit):

    HEAD              index           work-tree
--------------    --------------    --------------
README.md         README.md         README.md
main.py           main.py           main.py
web/migrate.sh    web/migrate.sh    web/migrate.sh

这些文件从何而来?好吧,当您第一次克隆存储库时,您的 Git 会从其他某个 Git 获取所有提交.这些提交在每个 Git 中完全相同,并且在每个 Git 中具有相同的哈希 ID.然后,您的 Git 将这些提交之一(您正在检出的提交)复制到 Git 的索引,并将文件从其索引复制到您的工作树.所以这就是每个文件的三个副本.

Where did these files come from? Well, when you first clone the repository, your Git gets all the commits from some other Git. Those commits are exactly the same in every Git, and have the same hash IDs across every Git. Your Git then copies one of those commits—the one you're checking out—to your Git's index, and copies the files from its index to your work-tree. So that's where you got three copies of each file.

工作树文件是普通的日常文件,您可以使用计算机上的任何程序读取和写入.其他文件不是.当(或之后)您对其中一个文件的工作树副本完成了一些工作时,您可以在其上运行 git add.这样做的原因是 git add 告诉 Git:使索引副本与工作树副本匹配.因此,如果您更改了 main.py,例如,索引中 main.pyversion 现在与 不同存储库中 main.py 的版本:

The work-tree files are ordinary everyday files, which you can read and write with any program on your computer. The other files are not. When (or after) you have done some work on the work-tree copy of one of your files, you run git add on it. The reason for this is that git add tells Git: make the index copy match the work-tree copy. So if you have changed main.py, for instance, the version of main.py in the index is now different from the version of main.py in the repository:

    HEAD              index           work-tree
--------------    --------------    --------------
README.md(1)      README.md(1)      README.md
main.py(1)        main.py(2)        main.py
web/migrate.sh(1) web/migrate.sh(1) web/migrate.sh

提交中的副本实际上是不可更改的,因此 HEAD(目前是 commit a123456... 的缩写)始终包含这三个文件的版本.但是索引虽然使用内部格式,但不是提交1,并且不是只读.所以 git add 可以替换索引副本.

The copy that's in a commit is literally unchangeable, so HEAD—which is short for commit a123456... at the moment—is always going to contain these three versions of the files. But the index, while it uses the internal format, is not a commit1 and is not read-only. So git add can replace the index copy.

(运行 git commit 获取索引中的任何内容并使用它来进行新的提交.然后新的提交成为当前提交,因此 name HEAD 和当前分支名称,现在指的是 new 提交,而不是提交 a123456....但我们不需要还没走那么远.)

(Running git commit takes whatever is in the index and uses that to make a new commit. The new commit then becomes the current commit, so that the name HEAD, and the current branch name, now refer to the new commit, instead of commit a123456.... But we don't need to go that far yet.)

1是什么,有点复杂,但大致来说,您可以将索引视为保存您提议的下一次提交em>.每次检出某个提交时,Git 必须设置索引以准备下一个提交:通常,通过从您刚刚检出的提交中填写它.

1What it is, is a bit complicated, but to a first approximation, you can think of the index as holding your proposed next commit. Every time you check out some commit, Git must set up the index to be ready for the next commit: normally, by filling it in from the commit you just checked out.

Git 索引中的文件副本采用压缩的、仅限 Git 的、去重的格式.工作树中文件的副本采用普通的日常计算机格式.因此,每当 Git 从 Git 的索引 复制到您的工作树时,它都必须扩展文件;并且任何时候 Git 将你的工作树复制到它的索引,它必须压缩和去重文件.

A copy of a file in Git's index is in the compressed, Git-only, de-duplicated format. A copy of a file in your work-tree is in ordinary everyday computer format. So any time Git copies from Git's index to your work-tree, it has to expand the file; and any time Git copies from your work-tree to its index, it has to compress and de-duplicate the file.

此复制过程是对文件进行任何更改的理想时机.所以这就是 .gitattributes 和行尾的东西发挥作用的地方.假设索引中的文件中,通过在存储库中到达那里,有换行符结束的行,只有\n.假设您希望文件的 work-tree 副本具有 \r\n 或 CRLF 行结尾.

This copying process is the ideal time to make any changes you want to the file. So this is where .gitattributes and line-ending stuff comes into play. Suppose the file in the index, which got there by being in the repository, has newline-terminated lines, with \n only. Suppose though that you'd like your work-tree copy of the file to have \r\n or CRLF line endings.

如果Git在索引的路上把\n变成了\r\n,并把\r\n\n 的方式in to 进行索引,这就达到了你的目的.这就是 * text eol=crlf 的作用.

If Git turns \n into \r\n on the way out of the index, and turns \r\n into \n on the way in to to index, this accomplishes your goal. That's what * text eol=crlf will do.

但是如果你不想那样呢?如果您希望 \n 结尾保留 \n 结尾怎么办?这就是 * text eol=lf 的作用.\n 结尾如何保持 \n 结尾?不进行任何更改.

But what if you don't want that? What if you want \n endings to remain \n endings? That's what * text eol=lf will do. How do \n endings remain \n endings? By not making any changes.

所以* text eol=lf 意味着不做更改.但是,如果存储库中的文件(因此被复制到索引中)具有 \r\n (CRLF) 行结尾怎么办?那么,您的工作树文件也是如此.

So * text eol=lf means do not make changes. But what if the file that's inside the repository, which is therefore copied into the index, has \r\n (CRLF) line endings? Well, then, so does your work-tree file.

要使 repository 中的某些文件具有 \n-only 行尾,您需要:

To make some files in the repository have \n-only line endings, you will need to:

  1. 从工作树副本中删除\r
  2. git add 结果文件;和
  3. git commit 进行新的提交.
  1. remove the \r from the work-tree copies;
  2. git add the resulting files; and
  3. git commit to make a new commit.

这个新提交然后可以分发到此存储库的所有其他副本,并用于代替那些文件具有 \r\n (CRLF) 结尾的现有(错误)提交.

This new commit can then be distributed out to all other copies of this repository, and used in place of the existing (bad) commit that has \r\n (CRLF) endings for those files.

请注意,错误提交将继续存在:这就是修订控制的全部内容.我们不会消除坏的,因为其他人也有它们,我们会记住他们使用的是坏的.

Note that the bad commit will continue to exist: that's what revision control is all about. We don't eliminate the bad ones, because everyone else has them too and we're going to remember that they are using a bad one.

现在,如果没有其他人拥有此存储库的副本,或者提交错误,那么我们处于特殊情况.在这种的情况下,我们可以放弃错误的提交以支持新的和改进的提交.(确切地说,如何在 Git 中执行此操作是另一个答案的主题.)但一般来说,我们只是添加了一个修复程序,并保留原来的.

Now, if no one else has a copy of this repository, or has the bad commit, then we're in a special-case situation. In this case we can drop the bad commit in favor of a new and improved commit. (Precisely how to do this in Git is a topic for another answer.) But in general, we just add a fix, and keep the original.

这篇关于为什么我的 .gitattributes 文件没有阻止在 Windows 上检出文件时添加“\r"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆