Reputation: 1158
How is that possible? I have a .gitattributes with the following content
* text=auto
UPDATE: Huh. It turns out it's .gitattributes file that results in this behavior. Can somebody explain this or point to the relevant docs?
Upvotes: 2
Views: 1157
Reputation: 1158
The git documentation is vague about how core.autocrlf
and .gitattributes play together:
text
This attribute enables and controls end-of-line normalization. When a text file is normalized, its line endings are converted to LF in the repository. To control what line ending style is used in the working directory, use the eol attribute for a single file and the core.eol configuration variable for all text files. Note that core.autocrlf overrides core.eol
What do they mean by Note that core.autocrlf overrides core.eol
if we have the following
core.eol
Sets the line ending type to use in the working directory for files that have the text property set when core.autocrlf is false. Alternatives are lf, crlf and native, which uses the platform’s native line ending. The default value is native. See gitattributes[5] for more information on end-of-line conversion.
This statement says that what I'm seeing is an expected behavior since core.eol
is native
by default and I have core.autocrlf=false
.
UPDATE:
This is indeed an expected behavior, core.eol
applies only to files with text
git attribute set and when autocrlf=false
. One can say that with autocrlf=false
* text=auto
triggers automatic line ending conversion (on Windows with default core.eol
the result is the same as autocrlf=true
).
The documentation is vague, but it will probably be improved soon.
Upvotes: 0
Reputation: 488203
After setting up a .gitattributes
, use git add --renormalize .
(from the directory that contains these *.sh
and *.bash
files) or git add --renormalize *.sh *.bash
to update the files before committing. Or, use touch *.sh *.bash; git checkout -f *.sh *.bash
to update the work-tree copies.
As you no doubt already know, a Git repository contains commits. Each commit has a frozen copy of each file that was committed, in exactly the state that it had when you (or whoever) committed it. This frozen copy can never be changed, so if it has CRLF line endings, it has them forever, and if it has LF-only line endings, it has them forever. Any other copy of that file, in any other future commit, can be different, but this copy in this commit is frozen. (Any copy in any other existing commit is of course also frozen, but could be different.)
Internally, each committed file is in a special, Git-only format, compressed and usable only by Git—and once committed, frozen forever in that particular commit. But of course, you can look at committed files, by extracting them; and you can make new committed files, working with the extracted files that you can modify. Thus, Git needs two operations:
It is these two operations that actually do any CRLF-to-LF-only or vice versa.
The place that holds the versions of files that you work with and work on is, perhaps unsurprisingly, called the work-tree (or some variant of this such as working tree or working directory). You use, and work on, files in your work-tree. You tell Git to copy files from a commit, to the work-tree, or to copy files from the work-tree, to (be ready for) a commit.
There's an extra wrinkle in the way here, and that is that Git doesn't make commits from what's in your work-tree at all. Instead, Git inserts, between the commit and the work-tree, a third holding-area. Git calls this the index, the staging area, or sometimes the cache, depending on who / which part of Git is doing the calling.
Files in the index are always ready to be committed. That is, they have the same special, compressed, Git-only format that they would in a commit. That's the trick that makes git commit
so fast (compared to other version control systems anyway): everything is, at all times—or almost all times anyway—ready to go. When you run git commit
, Git doesn't even look at the work-tree. It just packages up the files that are in the index, in the form they have now, all compressed and Git-ified and ready to go.
The git add
command copies files from the work-tree, into the index, making them ready-to-go. The git checkout
command, by comparison, copies files from a commit—the one you're checking out—first into the index so that they're ready for the next commit, and then on into the work-tree.
git add --renormalize
Suppose that some file is stored, in some way (with or without CRLF endings), in a commit. You run git checkout name
to pick a branch and its tip commit. The files in that commit go into the index, and from there to the work-tree. The copy-out step—index to work-tree—changes the files to have the line endings someone told Git to use, probably through a .gitattributes
file in the commit you just checked out.
If those are wrong, you now change the .gitattributes
file.1 This would, perhaps, change the way the files should be in the next commit. It would, perhaps, change the way the files should be in the work-tree. But—here's the problem—Git already has the files the way it thinks is right, in both the index and the work-tree.
Moreover, here's the worse problem: Not only does Git have the files the way it thinks is right, it also thinks it doesn't need to do any new work with them. If you run git checkout
or git add
on them right now, Git cleverly notices that the work-tree copies have not been touched and does nothing, even though a re-checkout or re-add would do something different!
The result is that you have to, in effect, trick Git into redoing work. If you need some or all work-tree files updated according to the "from index to work-tree" sequence, you can, for each such file:
git checkout
again, ortouch
the work-tree copy (so that Git thinks you've modified it) and run git checkout -f
to force-overwrite them.If you need some or all work-tree files updated according to the "from work-tree to index" sequence, you can:
touch
the work-tree copy (so that Git thinks you've modified it) and run git add
, orgit add --renormalize
to force Git to re-add the files even though it can see that you haven't touched them.If your Git is too old to have git add --renormalize
, you can use the touch
method.
1This all holds for core.autocrlf
and core.eol
as well, but it's almost always best to use the .gitattributes
file for finer control here. The Git maintainers do this for Git itself, for instance.
Upvotes: 3