git checkout branch ends up modifying files
I have a Mac on which I clone a repo. When I checkout any branch, the .json
files I have end up showing up as "modified".
Here is the list of things I tried.
git clone <blah>.git
git checkout release
git checkout -- $(git ls-files -m)
Files still show as modified:
me@box> cat ~/.gitattributes
text=auto
-crlf
me@box> cat .gitconfig
[core]
autocrlf = true
I have tried autocrlf
with auto
and false
, too.
Is there any way to fix this?
git
add a comment |
I have a Mac on which I clone a repo. When I checkout any branch, the .json
files I have end up showing up as "modified".
Here is the list of things I tried.
git clone <blah>.git
git checkout release
git checkout -- $(git ls-files -m)
Files still show as modified:
me@box> cat ~/.gitattributes
text=auto
-crlf
me@box> cat .gitconfig
[core]
autocrlf = true
I have tried autocrlf
with auto
and false
, too.
Is there any way to fix this?
git
I think you are on the right track, and the problem is the line endings.
– Tim Biegeleisen
Nov 21 '18 at 1:43
Is it ok to ask git to not do anything related to ELF on the files? Then try adding this to.git/info/attributes
:* -text
. That is telling git to use the files as they are on the repo. Can also be set on a .gitattributes file on the working tree.
– eftshift0
Nov 21 '18 at 1:43
Didn't help. Same problem, json files are modified.
– kgunjikar
Nov 21 '18 at 2:20
add a comment |
I have a Mac on which I clone a repo. When I checkout any branch, the .json
files I have end up showing up as "modified".
Here is the list of things I tried.
git clone <blah>.git
git checkout release
git checkout -- $(git ls-files -m)
Files still show as modified:
me@box> cat ~/.gitattributes
text=auto
-crlf
me@box> cat .gitconfig
[core]
autocrlf = true
I have tried autocrlf
with auto
and false
, too.
Is there any way to fix this?
git
I have a Mac on which I clone a repo. When I checkout any branch, the .json
files I have end up showing up as "modified".
Here is the list of things I tried.
git clone <blah>.git
git checkout release
git checkout -- $(git ls-files -m)
Files still show as modified:
me@box> cat ~/.gitattributes
text=auto
-crlf
me@box> cat .gitconfig
[core]
autocrlf = true
I have tried autocrlf
with auto
and false
, too.
Is there any way to fix this?
git
git
edited Nov 21 '18 at 1:45
paxdiablo
638k17212541677
638k17212541677
asked Nov 21 '18 at 1:40
kgunjikarkgunjikar
14810
14810
I think you are on the right track, and the problem is the line endings.
– Tim Biegeleisen
Nov 21 '18 at 1:43
Is it ok to ask git to not do anything related to ELF on the files? Then try adding this to.git/info/attributes
:* -text
. That is telling git to use the files as they are on the repo. Can also be set on a .gitattributes file on the working tree.
– eftshift0
Nov 21 '18 at 1:43
Didn't help. Same problem, json files are modified.
– kgunjikar
Nov 21 '18 at 2:20
add a comment |
I think you are on the right track, and the problem is the line endings.
– Tim Biegeleisen
Nov 21 '18 at 1:43
Is it ok to ask git to not do anything related to ELF on the files? Then try adding this to.git/info/attributes
:* -text
. That is telling git to use the files as they are on the repo. Can also be set on a .gitattributes file on the working tree.
– eftshift0
Nov 21 '18 at 1:43
Didn't help. Same problem, json files are modified.
– kgunjikar
Nov 21 '18 at 2:20
I think you are on the right track, and the problem is the line endings.
– Tim Biegeleisen
Nov 21 '18 at 1:43
I think you are on the right track, and the problem is the line endings.
– Tim Biegeleisen
Nov 21 '18 at 1:43
Is it ok to ask git to not do anything related to ELF on the files? Then try adding this to
.git/info/attributes
: * -text
. That is telling git to use the files as they are on the repo. Can also be set on a .gitattributes file on the working tree.– eftshift0
Nov 21 '18 at 1:43
Is it ok to ask git to not do anything related to ELF on the files? Then try adding this to
.git/info/attributes
: * -text
. That is telling git to use the files as they are on the repo. Can also be set on a .gitattributes file on the working tree.– eftshift0
Nov 21 '18 at 1:43
Didn't help. Same problem, json files are modified.
– kgunjikar
Nov 21 '18 at 2:20
Didn't help. Same problem, json files are modified.
– kgunjikar
Nov 21 '18 at 2:20
add a comment |
1 Answer
1
active
oldest
votes
TL;DR
You may want .gitattributes
(not ~/.gitattributes
) to contain the line * -text
or *.json -text
. You may want to remove any autocrlf = true
setting from .git/config
or $XDG_CONFIG_HOME/git/config
or $HOME/.config/git/config
or $HOME/.gitconfig
. You might want finer control of additional files, depending on how you and others are using these repositories, on which systems.
Long
For one thing, these lines are wrong:
text=auto
-crlf
The entries in .gitattributes
should have the form of a file name or pattern, followed by white space (typically a tab but blanks are OK too), followed by one or more attributes. Here, the file name / pattern part is missing, though Git does not see it that way: what this tells Git to do is to apply no attributes to all files named text=auto
, and apply no attribute to all files named -crlf
. Since there probably are no files with these funny names, this applies no attributes to no files—so it has no effect.
(You probably wanted * text=auto
and/or * -crlf
, but see below.)
Similarly (but perhaps not the problem), this might be the wrong place:
cat ~/.gitattributes
Git looks for attribute files in several places. The most important one is .gitattributes
in the top level of the repository work-tree, i.e., .gitattributes
, not ~/.gitattributes
. You can have Git consult core.attributesFile
, which you can set to ~/.gitattributes
. Modern Git looks for $XDG_CONFIG_HOME/git/attributes
or $HOME/.config/git/attributes
if core.attributesFile
is not set, so unless you configured your global core.attributesFile
setting, it won't be looking in $HOME/.gitattributes
.
Opinion: core.autocrlf
is not a good idea
cat .gitconfig
[core]
autocrlf = true
If this was from your home directory, this may be the source of the problem.
Setting core.autocrlf
to true
acts this way, as described in the git config
documentation:
Setting this variable to "true" is the same as setting the
text
attribute to "auto" on all files andcore.eol
to "crlf". Set to
true if you want to have CRLF line endings in your working
directory and the repository has LF line endings. This variable can
be set to input, in which case no output conversion is performed.
In general, I dislike text=auto
, because it means Git has to guess whether your file is text. Will Git guess wrong? Sometimes, yes, it will. If you are on Linux, where the end-of-line conversion defaults to do nothing, this is harmless, but on Windows and MacOS, it's not so harmless. Here, a .gitattributes
file listing specifically which line endings go in which files is useful.
It's worth looking at the core.eol
description as well, since you mentioned setting core.autocrlf
to false
:
Sets the line ending type to use in the working directory for files
that have the text property set whencore.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.
I have tried
autocrlf
withauto
andfalse
, too.
The only valid settings are true
, false
, and input
. Well, technically, setting it to 1
, 0
, and -1
respectively also work, as Git will try to convert it as an integer if it does not match one of these words. Setting it to the literal string auto
makes Git treat it as an integer, which converts to the value 0
, and hence means false
. That's the default, in which case core.eol
comes into play provided there are no overrides in .gitattributes
files.
Much of the remaining things to know are buried in that gitattributes
documentation, except for a few key items that are not properly described anywhere. These require a clear definition of Git's index and work-tree.
Commits, the index, and the work-tree
The first thing to realize is that any committed file is frozen into whatever data is in the committed form of that file, forever that way in that particular commit. That is, each commit holds a snapshot of every file—well, every file that's in that commit, but that seems a bit redundant—in the form it had in the index when whoever made the snapshot, made the snapshot (by running git commit
).
If a committed file has a line that ends with CR-then-LF, followed by a second line that ends with CR-without-LF, followed by a third line that ends with LF-without-CR, that committed version of that file always has that form. There is nothing anyone can do about this. If you have that commit, you have that file, with those three line endings in that order. You can choose to check it out, or not; but whatever you do, or don't do, that file exists in that form in that commit.
But wait! The act of checking out some file—or a commit full of files, for that matter—does not mean put that file into the work-tree as-is. Let's consider a file such as x.json
. First, the file has to go through your index. Git's index is a special data structure (stored in one big flat file, usually, although there are some optimizations that use more than one file sometimes: the flat file has various index slots, found partly by names like x.json
, along with linkages to the internal data, which is really stored elsewhere: the index copy is actually just a pointer to the real data). The index has two more names, reflecting, perhaps, its importance in Git, or perhaps that the name index leaves something to be desired: it's also called the staging area, or sometimes the cache, depending on who / what part of Git is doing the calling.
In any case, the x.json
inside the commit is frozen, as we just mentioned, but also is in a special, compressed (sometimes highly compressed) Git-only format. Git first copies that x.json
to your index, unfreezing the file, but keeping it in the special Git-only format. Since the copy in your index currently matches the copy in the commit, it has the same line-endings. But since it's not frozen, you can change the line endings, if you like. Before we get there, let's look at the last step of checking out x.json
.
The Git-only file is useless to anything other than Git. So, Git copies the unfrozen, Git-only file from the index to your work-tree, creating an actual, ordinary-format, x.json
file. It's this last step that does the first pass of end-of-line manipulation, based on:
- whether the file has been detected as "text"—a non-text file is never manipulated this way—and
- the
eol
setting, if there is any, from the.gitattributes
file or implied bycore.eol
orcore.autocrlf
, more or less in that order.
You can, of course, also copy from the work-tree to the index. At this time, Git will once again do end-of-line manipulation, based on those same two items, plus the input
setting, which tells Git to do end-of-line conversions even if the output pass didn't.
The conversions
There are actually two possible conversions. The first, and most common, have to do with carriage-return and line-feed line endings. The second, which is new since commit 107642fe2661 (first appearing in Git 2.18.0), has to do with Unicode encodings.
CRLF conversions
While copying from index to work-tree, Git will replace LF-only line endings with CRLF line endings if you have told it to do so. It will not replace CR-LF with LF; it only does LF-to-CRLF here.
While copying from work-tree to index, Git will replace CRLF line endings with LF-only line endings if you have told it to do so. It will not replace LF with CRLF; it does onyl CRLF-to-LF here. There is some extra magic in modern Git: if the index copy of the file has any carriage-return (r
or ^M
characters), Git inhibits conversion unless it's doing a "renormalize" operation (git add --renormalize
, plus a merge or cherry-pick with the renormalize flag turned on).
Unicode conversions
As I mentioned, these were new in Git 2.18 (and are still new to me). Git will now re-encode UTF-16 files into UTF-8, and vice versa, if you tell it to, through a .gitattributes
entry. As with the end of line conversions, these are directional: git add
, which copies from work-tree to index, prepares the file for storage inside Git and will convert to UTF-8, while git checkout
or git checkout-index
(or various additional cases like git reset --hard
), which copies from index to work-tree, will convert from UTF-8.
Additional re-encodings may be available, based on whatever iconv --list
prints. This, too, is described in the gitattributes documentation.
Changing text=
and conversion-bypassing issues
The default for Git is to guess whether some file is text or not. If a file is text, it gets converted; if not, Git treats it as sacrosanct: whatever is in the index goes into the work-tree, and whatever is in the work-tree goes into the index. So * -text
tells Git: never touch any file at all, and there are no issues. But what if Git was previously thinking * text=auto
?
The answer is to this question is: this does not work well. Bad things happen! Except for git add --renormalize
, Git will believe that the index and work-tree match—and hence not bother to copy one way or the other, if the file was recently extracted or added.
That is, in particular, watch out for this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git add file.ext # use whatever's in the work-tree
and this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git checkout -- file.ext # see what's actually in the commit
The git add
or git checkout
step here may do nothing on the assumption that the work-tree and index already match, even if they only used to match based on the previous .gitattributes
setting (or lack thereof). You can force the extraction, or the adding, by modifying (or even removing, if you're going to re-extract) the file, so that the work-tree copy is now clearly different. But in general, it's annoying and tricky to get a .gitattributes
change to take effect, because Git doesn't realize that modifying .gitattributes
can change the conversions Git will perform.
For that matter, the same is true of core.eol
and core.autocrlf
: changing these configuration items changes the conversion Git might perform, yet Git is not aware that the index and work-tree may now be out of sync with each other. The index's cache aspect works against you here.
Finally, note that Git will tell you that some file is or would be modified if Git sees that git add
-ing the file would change the line endings. Sometimes Git guesses wrong, especially after altering .gitattributes
entries so that files that were classified as text are now classified as binary, or vice versa. In these cases, you have to git add
the file anyway, so that Git updates the index copy, after which Git realizes that the updated index copy still matches the HEAD
commit and that the file isn't modified after all.
In other words, sometimes fixing the .gitattributes
file is not sufficient, even if the file isn't going to change. Note also that sometimes, the committed version of the file has the wrong line endings if the .gitattributes
entry was missing or incorrect. The work-tree copy may well be right, but until you make a new commit that has the frozen copy built from a correct index copy, Git will—this time correctly—tell you that the file is modified!
Regarding the UTF-8 conversion: stackoverflow.com/a/50435869/6309
– VonC
Nov 21 '18 at 5:46
Thanks torek will try and get back
– kgunjikar
Nov 21 '18 at 18:55
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53404150%2fgit-checkout-branch-ends-up-modifying-files%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
TL;DR
You may want .gitattributes
(not ~/.gitattributes
) to contain the line * -text
or *.json -text
. You may want to remove any autocrlf = true
setting from .git/config
or $XDG_CONFIG_HOME/git/config
or $HOME/.config/git/config
or $HOME/.gitconfig
. You might want finer control of additional files, depending on how you and others are using these repositories, on which systems.
Long
For one thing, these lines are wrong:
text=auto
-crlf
The entries in .gitattributes
should have the form of a file name or pattern, followed by white space (typically a tab but blanks are OK too), followed by one or more attributes. Here, the file name / pattern part is missing, though Git does not see it that way: what this tells Git to do is to apply no attributes to all files named text=auto
, and apply no attribute to all files named -crlf
. Since there probably are no files with these funny names, this applies no attributes to no files—so it has no effect.
(You probably wanted * text=auto
and/or * -crlf
, but see below.)
Similarly (but perhaps not the problem), this might be the wrong place:
cat ~/.gitattributes
Git looks for attribute files in several places. The most important one is .gitattributes
in the top level of the repository work-tree, i.e., .gitattributes
, not ~/.gitattributes
. You can have Git consult core.attributesFile
, which you can set to ~/.gitattributes
. Modern Git looks for $XDG_CONFIG_HOME/git/attributes
or $HOME/.config/git/attributes
if core.attributesFile
is not set, so unless you configured your global core.attributesFile
setting, it won't be looking in $HOME/.gitattributes
.
Opinion: core.autocrlf
is not a good idea
cat .gitconfig
[core]
autocrlf = true
If this was from your home directory, this may be the source of the problem.
Setting core.autocrlf
to true
acts this way, as described in the git config
documentation:
Setting this variable to "true" is the same as setting the
text
attribute to "auto" on all files andcore.eol
to "crlf". Set to
true if you want to have CRLF line endings in your working
directory and the repository has LF line endings. This variable can
be set to input, in which case no output conversion is performed.
In general, I dislike text=auto
, because it means Git has to guess whether your file is text. Will Git guess wrong? Sometimes, yes, it will. If you are on Linux, where the end-of-line conversion defaults to do nothing, this is harmless, but on Windows and MacOS, it's not so harmless. Here, a .gitattributes
file listing specifically which line endings go in which files is useful.
It's worth looking at the core.eol
description as well, since you mentioned setting core.autocrlf
to false
:
Sets the line ending type to use in the working directory for files
that have the text property set whencore.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.
I have tried
autocrlf
withauto
andfalse
, too.
The only valid settings are true
, false
, and input
. Well, technically, setting it to 1
, 0
, and -1
respectively also work, as Git will try to convert it as an integer if it does not match one of these words. Setting it to the literal string auto
makes Git treat it as an integer, which converts to the value 0
, and hence means false
. That's the default, in which case core.eol
comes into play provided there are no overrides in .gitattributes
files.
Much of the remaining things to know are buried in that gitattributes
documentation, except for a few key items that are not properly described anywhere. These require a clear definition of Git's index and work-tree.
Commits, the index, and the work-tree
The first thing to realize is that any committed file is frozen into whatever data is in the committed form of that file, forever that way in that particular commit. That is, each commit holds a snapshot of every file—well, every file that's in that commit, but that seems a bit redundant—in the form it had in the index when whoever made the snapshot, made the snapshot (by running git commit
).
If a committed file has a line that ends with CR-then-LF, followed by a second line that ends with CR-without-LF, followed by a third line that ends with LF-without-CR, that committed version of that file always has that form. There is nothing anyone can do about this. If you have that commit, you have that file, with those three line endings in that order. You can choose to check it out, or not; but whatever you do, or don't do, that file exists in that form in that commit.
But wait! The act of checking out some file—or a commit full of files, for that matter—does not mean put that file into the work-tree as-is. Let's consider a file such as x.json
. First, the file has to go through your index. Git's index is a special data structure (stored in one big flat file, usually, although there are some optimizations that use more than one file sometimes: the flat file has various index slots, found partly by names like x.json
, along with linkages to the internal data, which is really stored elsewhere: the index copy is actually just a pointer to the real data). The index has two more names, reflecting, perhaps, its importance in Git, or perhaps that the name index leaves something to be desired: it's also called the staging area, or sometimes the cache, depending on who / what part of Git is doing the calling.
In any case, the x.json
inside the commit is frozen, as we just mentioned, but also is in a special, compressed (sometimes highly compressed) Git-only format. Git first copies that x.json
to your index, unfreezing the file, but keeping it in the special Git-only format. Since the copy in your index currently matches the copy in the commit, it has the same line-endings. But since it's not frozen, you can change the line endings, if you like. Before we get there, let's look at the last step of checking out x.json
.
The Git-only file is useless to anything other than Git. So, Git copies the unfrozen, Git-only file from the index to your work-tree, creating an actual, ordinary-format, x.json
file. It's this last step that does the first pass of end-of-line manipulation, based on:
- whether the file has been detected as "text"—a non-text file is never manipulated this way—and
- the
eol
setting, if there is any, from the.gitattributes
file or implied bycore.eol
orcore.autocrlf
, more or less in that order.
You can, of course, also copy from the work-tree to the index. At this time, Git will once again do end-of-line manipulation, based on those same two items, plus the input
setting, which tells Git to do end-of-line conversions even if the output pass didn't.
The conversions
There are actually two possible conversions. The first, and most common, have to do with carriage-return and line-feed line endings. The second, which is new since commit 107642fe2661 (first appearing in Git 2.18.0), has to do with Unicode encodings.
CRLF conversions
While copying from index to work-tree, Git will replace LF-only line endings with CRLF line endings if you have told it to do so. It will not replace CR-LF with LF; it only does LF-to-CRLF here.
While copying from work-tree to index, Git will replace CRLF line endings with LF-only line endings if you have told it to do so. It will not replace LF with CRLF; it does onyl CRLF-to-LF here. There is some extra magic in modern Git: if the index copy of the file has any carriage-return (r
or ^M
characters), Git inhibits conversion unless it's doing a "renormalize" operation (git add --renormalize
, plus a merge or cherry-pick with the renormalize flag turned on).
Unicode conversions
As I mentioned, these were new in Git 2.18 (and are still new to me). Git will now re-encode UTF-16 files into UTF-8, and vice versa, if you tell it to, through a .gitattributes
entry. As with the end of line conversions, these are directional: git add
, which copies from work-tree to index, prepares the file for storage inside Git and will convert to UTF-8, while git checkout
or git checkout-index
(or various additional cases like git reset --hard
), which copies from index to work-tree, will convert from UTF-8.
Additional re-encodings may be available, based on whatever iconv --list
prints. This, too, is described in the gitattributes documentation.
Changing text=
and conversion-bypassing issues
The default for Git is to guess whether some file is text or not. If a file is text, it gets converted; if not, Git treats it as sacrosanct: whatever is in the index goes into the work-tree, and whatever is in the work-tree goes into the index. So * -text
tells Git: never touch any file at all, and there are no issues. But what if Git was previously thinking * text=auto
?
The answer is to this question is: this does not work well. Bad things happen! Except for git add --renormalize
, Git will believe that the index and work-tree match—and hence not bother to copy one way or the other, if the file was recently extracted or added.
That is, in particular, watch out for this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git add file.ext # use whatever's in the work-tree
and this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git checkout -- file.ext # see what's actually in the commit
The git add
or git checkout
step here may do nothing on the assumption that the work-tree and index already match, even if they only used to match based on the previous .gitattributes
setting (or lack thereof). You can force the extraction, or the adding, by modifying (or even removing, if you're going to re-extract) the file, so that the work-tree copy is now clearly different. But in general, it's annoying and tricky to get a .gitattributes
change to take effect, because Git doesn't realize that modifying .gitattributes
can change the conversions Git will perform.
For that matter, the same is true of core.eol
and core.autocrlf
: changing these configuration items changes the conversion Git might perform, yet Git is not aware that the index and work-tree may now be out of sync with each other. The index's cache aspect works against you here.
Finally, note that Git will tell you that some file is or would be modified if Git sees that git add
-ing the file would change the line endings. Sometimes Git guesses wrong, especially after altering .gitattributes
entries so that files that were classified as text are now classified as binary, or vice versa. In these cases, you have to git add
the file anyway, so that Git updates the index copy, after which Git realizes that the updated index copy still matches the HEAD
commit and that the file isn't modified after all.
In other words, sometimes fixing the .gitattributes
file is not sufficient, even if the file isn't going to change. Note also that sometimes, the committed version of the file has the wrong line endings if the .gitattributes
entry was missing or incorrect. The work-tree copy may well be right, but until you make a new commit that has the frozen copy built from a correct index copy, Git will—this time correctly—tell you that the file is modified!
Regarding the UTF-8 conversion: stackoverflow.com/a/50435869/6309
– VonC
Nov 21 '18 at 5:46
Thanks torek will try and get back
– kgunjikar
Nov 21 '18 at 18:55
add a comment |
TL;DR
You may want .gitattributes
(not ~/.gitattributes
) to contain the line * -text
or *.json -text
. You may want to remove any autocrlf = true
setting from .git/config
or $XDG_CONFIG_HOME/git/config
or $HOME/.config/git/config
or $HOME/.gitconfig
. You might want finer control of additional files, depending on how you and others are using these repositories, on which systems.
Long
For one thing, these lines are wrong:
text=auto
-crlf
The entries in .gitattributes
should have the form of a file name or pattern, followed by white space (typically a tab but blanks are OK too), followed by one or more attributes. Here, the file name / pattern part is missing, though Git does not see it that way: what this tells Git to do is to apply no attributes to all files named text=auto
, and apply no attribute to all files named -crlf
. Since there probably are no files with these funny names, this applies no attributes to no files—so it has no effect.
(You probably wanted * text=auto
and/or * -crlf
, but see below.)
Similarly (but perhaps not the problem), this might be the wrong place:
cat ~/.gitattributes
Git looks for attribute files in several places. The most important one is .gitattributes
in the top level of the repository work-tree, i.e., .gitattributes
, not ~/.gitattributes
. You can have Git consult core.attributesFile
, which you can set to ~/.gitattributes
. Modern Git looks for $XDG_CONFIG_HOME/git/attributes
or $HOME/.config/git/attributes
if core.attributesFile
is not set, so unless you configured your global core.attributesFile
setting, it won't be looking in $HOME/.gitattributes
.
Opinion: core.autocrlf
is not a good idea
cat .gitconfig
[core]
autocrlf = true
If this was from your home directory, this may be the source of the problem.
Setting core.autocrlf
to true
acts this way, as described in the git config
documentation:
Setting this variable to "true" is the same as setting the
text
attribute to "auto" on all files andcore.eol
to "crlf". Set to
true if you want to have CRLF line endings in your working
directory and the repository has LF line endings. This variable can
be set to input, in which case no output conversion is performed.
In general, I dislike text=auto
, because it means Git has to guess whether your file is text. Will Git guess wrong? Sometimes, yes, it will. If you are on Linux, where the end-of-line conversion defaults to do nothing, this is harmless, but on Windows and MacOS, it's not so harmless. Here, a .gitattributes
file listing specifically which line endings go in which files is useful.
It's worth looking at the core.eol
description as well, since you mentioned setting core.autocrlf
to false
:
Sets the line ending type to use in the working directory for files
that have the text property set whencore.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.
I have tried
autocrlf
withauto
andfalse
, too.
The only valid settings are true
, false
, and input
. Well, technically, setting it to 1
, 0
, and -1
respectively also work, as Git will try to convert it as an integer if it does not match one of these words. Setting it to the literal string auto
makes Git treat it as an integer, which converts to the value 0
, and hence means false
. That's the default, in which case core.eol
comes into play provided there are no overrides in .gitattributes
files.
Much of the remaining things to know are buried in that gitattributes
documentation, except for a few key items that are not properly described anywhere. These require a clear definition of Git's index and work-tree.
Commits, the index, and the work-tree
The first thing to realize is that any committed file is frozen into whatever data is in the committed form of that file, forever that way in that particular commit. That is, each commit holds a snapshot of every file—well, every file that's in that commit, but that seems a bit redundant—in the form it had in the index when whoever made the snapshot, made the snapshot (by running git commit
).
If a committed file has a line that ends with CR-then-LF, followed by a second line that ends with CR-without-LF, followed by a third line that ends with LF-without-CR, that committed version of that file always has that form. There is nothing anyone can do about this. If you have that commit, you have that file, with those three line endings in that order. You can choose to check it out, or not; but whatever you do, or don't do, that file exists in that form in that commit.
But wait! The act of checking out some file—or a commit full of files, for that matter—does not mean put that file into the work-tree as-is. Let's consider a file such as x.json
. First, the file has to go through your index. Git's index is a special data structure (stored in one big flat file, usually, although there are some optimizations that use more than one file sometimes: the flat file has various index slots, found partly by names like x.json
, along with linkages to the internal data, which is really stored elsewhere: the index copy is actually just a pointer to the real data). The index has two more names, reflecting, perhaps, its importance in Git, or perhaps that the name index leaves something to be desired: it's also called the staging area, or sometimes the cache, depending on who / what part of Git is doing the calling.
In any case, the x.json
inside the commit is frozen, as we just mentioned, but also is in a special, compressed (sometimes highly compressed) Git-only format. Git first copies that x.json
to your index, unfreezing the file, but keeping it in the special Git-only format. Since the copy in your index currently matches the copy in the commit, it has the same line-endings. But since it's not frozen, you can change the line endings, if you like. Before we get there, let's look at the last step of checking out x.json
.
The Git-only file is useless to anything other than Git. So, Git copies the unfrozen, Git-only file from the index to your work-tree, creating an actual, ordinary-format, x.json
file. It's this last step that does the first pass of end-of-line manipulation, based on:
- whether the file has been detected as "text"—a non-text file is never manipulated this way—and
- the
eol
setting, if there is any, from the.gitattributes
file or implied bycore.eol
orcore.autocrlf
, more or less in that order.
You can, of course, also copy from the work-tree to the index. At this time, Git will once again do end-of-line manipulation, based on those same two items, plus the input
setting, which tells Git to do end-of-line conversions even if the output pass didn't.
The conversions
There are actually two possible conversions. The first, and most common, have to do with carriage-return and line-feed line endings. The second, which is new since commit 107642fe2661 (first appearing in Git 2.18.0), has to do with Unicode encodings.
CRLF conversions
While copying from index to work-tree, Git will replace LF-only line endings with CRLF line endings if you have told it to do so. It will not replace CR-LF with LF; it only does LF-to-CRLF here.
While copying from work-tree to index, Git will replace CRLF line endings with LF-only line endings if you have told it to do so. It will not replace LF with CRLF; it does onyl CRLF-to-LF here. There is some extra magic in modern Git: if the index copy of the file has any carriage-return (r
or ^M
characters), Git inhibits conversion unless it's doing a "renormalize" operation (git add --renormalize
, plus a merge or cherry-pick with the renormalize flag turned on).
Unicode conversions
As I mentioned, these were new in Git 2.18 (and are still new to me). Git will now re-encode UTF-16 files into UTF-8, and vice versa, if you tell it to, through a .gitattributes
entry. As with the end of line conversions, these are directional: git add
, which copies from work-tree to index, prepares the file for storage inside Git and will convert to UTF-8, while git checkout
or git checkout-index
(or various additional cases like git reset --hard
), which copies from index to work-tree, will convert from UTF-8.
Additional re-encodings may be available, based on whatever iconv --list
prints. This, too, is described in the gitattributes documentation.
Changing text=
and conversion-bypassing issues
The default for Git is to guess whether some file is text or not. If a file is text, it gets converted; if not, Git treats it as sacrosanct: whatever is in the index goes into the work-tree, and whatever is in the work-tree goes into the index. So * -text
tells Git: never touch any file at all, and there are no issues. But what if Git was previously thinking * text=auto
?
The answer is to this question is: this does not work well. Bad things happen! Except for git add --renormalize
, Git will believe that the index and work-tree match—and hence not bother to copy one way or the other, if the file was recently extracted or added.
That is, in particular, watch out for this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git add file.ext # use whatever's in the work-tree
and this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git checkout -- file.ext # see what's actually in the commit
The git add
or git checkout
step here may do nothing on the assumption that the work-tree and index already match, even if they only used to match based on the previous .gitattributes
setting (or lack thereof). You can force the extraction, or the adding, by modifying (or even removing, if you're going to re-extract) the file, so that the work-tree copy is now clearly different. But in general, it's annoying and tricky to get a .gitattributes
change to take effect, because Git doesn't realize that modifying .gitattributes
can change the conversions Git will perform.
For that matter, the same is true of core.eol
and core.autocrlf
: changing these configuration items changes the conversion Git might perform, yet Git is not aware that the index and work-tree may now be out of sync with each other. The index's cache aspect works against you here.
Finally, note that Git will tell you that some file is or would be modified if Git sees that git add
-ing the file would change the line endings. Sometimes Git guesses wrong, especially after altering .gitattributes
entries so that files that were classified as text are now classified as binary, or vice versa. In these cases, you have to git add
the file anyway, so that Git updates the index copy, after which Git realizes that the updated index copy still matches the HEAD
commit and that the file isn't modified after all.
In other words, sometimes fixing the .gitattributes
file is not sufficient, even if the file isn't going to change. Note also that sometimes, the committed version of the file has the wrong line endings if the .gitattributes
entry was missing or incorrect. The work-tree copy may well be right, but until you make a new commit that has the frozen copy built from a correct index copy, Git will—this time correctly—tell you that the file is modified!
Regarding the UTF-8 conversion: stackoverflow.com/a/50435869/6309
– VonC
Nov 21 '18 at 5:46
Thanks torek will try and get back
– kgunjikar
Nov 21 '18 at 18:55
add a comment |
TL;DR
You may want .gitattributes
(not ~/.gitattributes
) to contain the line * -text
or *.json -text
. You may want to remove any autocrlf = true
setting from .git/config
or $XDG_CONFIG_HOME/git/config
or $HOME/.config/git/config
or $HOME/.gitconfig
. You might want finer control of additional files, depending on how you and others are using these repositories, on which systems.
Long
For one thing, these lines are wrong:
text=auto
-crlf
The entries in .gitattributes
should have the form of a file name or pattern, followed by white space (typically a tab but blanks are OK too), followed by one or more attributes. Here, the file name / pattern part is missing, though Git does not see it that way: what this tells Git to do is to apply no attributes to all files named text=auto
, and apply no attribute to all files named -crlf
. Since there probably are no files with these funny names, this applies no attributes to no files—so it has no effect.
(You probably wanted * text=auto
and/or * -crlf
, but see below.)
Similarly (but perhaps not the problem), this might be the wrong place:
cat ~/.gitattributes
Git looks for attribute files in several places. The most important one is .gitattributes
in the top level of the repository work-tree, i.e., .gitattributes
, not ~/.gitattributes
. You can have Git consult core.attributesFile
, which you can set to ~/.gitattributes
. Modern Git looks for $XDG_CONFIG_HOME/git/attributes
or $HOME/.config/git/attributes
if core.attributesFile
is not set, so unless you configured your global core.attributesFile
setting, it won't be looking in $HOME/.gitattributes
.
Opinion: core.autocrlf
is not a good idea
cat .gitconfig
[core]
autocrlf = true
If this was from your home directory, this may be the source of the problem.
Setting core.autocrlf
to true
acts this way, as described in the git config
documentation:
Setting this variable to "true" is the same as setting the
text
attribute to "auto" on all files andcore.eol
to "crlf". Set to
true if you want to have CRLF line endings in your working
directory and the repository has LF line endings. This variable can
be set to input, in which case no output conversion is performed.
In general, I dislike text=auto
, because it means Git has to guess whether your file is text. Will Git guess wrong? Sometimes, yes, it will. If you are on Linux, where the end-of-line conversion defaults to do nothing, this is harmless, but on Windows and MacOS, it's not so harmless. Here, a .gitattributes
file listing specifically which line endings go in which files is useful.
It's worth looking at the core.eol
description as well, since you mentioned setting core.autocrlf
to false
:
Sets the line ending type to use in the working directory for files
that have the text property set whencore.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.
I have tried
autocrlf
withauto
andfalse
, too.
The only valid settings are true
, false
, and input
. Well, technically, setting it to 1
, 0
, and -1
respectively also work, as Git will try to convert it as an integer if it does not match one of these words. Setting it to the literal string auto
makes Git treat it as an integer, which converts to the value 0
, and hence means false
. That's the default, in which case core.eol
comes into play provided there are no overrides in .gitattributes
files.
Much of the remaining things to know are buried in that gitattributes
documentation, except for a few key items that are not properly described anywhere. These require a clear definition of Git's index and work-tree.
Commits, the index, and the work-tree
The first thing to realize is that any committed file is frozen into whatever data is in the committed form of that file, forever that way in that particular commit. That is, each commit holds a snapshot of every file—well, every file that's in that commit, but that seems a bit redundant—in the form it had in the index when whoever made the snapshot, made the snapshot (by running git commit
).
If a committed file has a line that ends with CR-then-LF, followed by a second line that ends with CR-without-LF, followed by a third line that ends with LF-without-CR, that committed version of that file always has that form. There is nothing anyone can do about this. If you have that commit, you have that file, with those three line endings in that order. You can choose to check it out, or not; but whatever you do, or don't do, that file exists in that form in that commit.
But wait! The act of checking out some file—or a commit full of files, for that matter—does not mean put that file into the work-tree as-is. Let's consider a file such as x.json
. First, the file has to go through your index. Git's index is a special data structure (stored in one big flat file, usually, although there are some optimizations that use more than one file sometimes: the flat file has various index slots, found partly by names like x.json
, along with linkages to the internal data, which is really stored elsewhere: the index copy is actually just a pointer to the real data). The index has two more names, reflecting, perhaps, its importance in Git, or perhaps that the name index leaves something to be desired: it's also called the staging area, or sometimes the cache, depending on who / what part of Git is doing the calling.
In any case, the x.json
inside the commit is frozen, as we just mentioned, but also is in a special, compressed (sometimes highly compressed) Git-only format. Git first copies that x.json
to your index, unfreezing the file, but keeping it in the special Git-only format. Since the copy in your index currently matches the copy in the commit, it has the same line-endings. But since it's not frozen, you can change the line endings, if you like. Before we get there, let's look at the last step of checking out x.json
.
The Git-only file is useless to anything other than Git. So, Git copies the unfrozen, Git-only file from the index to your work-tree, creating an actual, ordinary-format, x.json
file. It's this last step that does the first pass of end-of-line manipulation, based on:
- whether the file has been detected as "text"—a non-text file is never manipulated this way—and
- the
eol
setting, if there is any, from the.gitattributes
file or implied bycore.eol
orcore.autocrlf
, more or less in that order.
You can, of course, also copy from the work-tree to the index. At this time, Git will once again do end-of-line manipulation, based on those same two items, plus the input
setting, which tells Git to do end-of-line conversions even if the output pass didn't.
The conversions
There are actually two possible conversions. The first, and most common, have to do with carriage-return and line-feed line endings. The second, which is new since commit 107642fe2661 (first appearing in Git 2.18.0), has to do with Unicode encodings.
CRLF conversions
While copying from index to work-tree, Git will replace LF-only line endings with CRLF line endings if you have told it to do so. It will not replace CR-LF with LF; it only does LF-to-CRLF here.
While copying from work-tree to index, Git will replace CRLF line endings with LF-only line endings if you have told it to do so. It will not replace LF with CRLF; it does onyl CRLF-to-LF here. There is some extra magic in modern Git: if the index copy of the file has any carriage-return (r
or ^M
characters), Git inhibits conversion unless it's doing a "renormalize" operation (git add --renormalize
, plus a merge or cherry-pick with the renormalize flag turned on).
Unicode conversions
As I mentioned, these were new in Git 2.18 (and are still new to me). Git will now re-encode UTF-16 files into UTF-8, and vice versa, if you tell it to, through a .gitattributes
entry. As with the end of line conversions, these are directional: git add
, which copies from work-tree to index, prepares the file for storage inside Git and will convert to UTF-8, while git checkout
or git checkout-index
(or various additional cases like git reset --hard
), which copies from index to work-tree, will convert from UTF-8.
Additional re-encodings may be available, based on whatever iconv --list
prints. This, too, is described in the gitattributes documentation.
Changing text=
and conversion-bypassing issues
The default for Git is to guess whether some file is text or not. If a file is text, it gets converted; if not, Git treats it as sacrosanct: whatever is in the index goes into the work-tree, and whatever is in the work-tree goes into the index. So * -text
tells Git: never touch any file at all, and there are no issues. But what if Git was previously thinking * text=auto
?
The answer is to this question is: this does not work well. Bad things happen! Except for git add --renormalize
, Git will believe that the index and work-tree match—and hence not bother to copy one way or the other, if the file was recently extracted or added.
That is, in particular, watch out for this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git add file.ext # use whatever's in the work-tree
and this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git checkout -- file.ext # see what's actually in the commit
The git add
or git checkout
step here may do nothing on the assumption that the work-tree and index already match, even if they only used to match based on the previous .gitattributes
setting (or lack thereof). You can force the extraction, or the adding, by modifying (or even removing, if you're going to re-extract) the file, so that the work-tree copy is now clearly different. But in general, it's annoying and tricky to get a .gitattributes
change to take effect, because Git doesn't realize that modifying .gitattributes
can change the conversions Git will perform.
For that matter, the same is true of core.eol
and core.autocrlf
: changing these configuration items changes the conversion Git might perform, yet Git is not aware that the index and work-tree may now be out of sync with each other. The index's cache aspect works against you here.
Finally, note that Git will tell you that some file is or would be modified if Git sees that git add
-ing the file would change the line endings. Sometimes Git guesses wrong, especially after altering .gitattributes
entries so that files that were classified as text are now classified as binary, or vice versa. In these cases, you have to git add
the file anyway, so that Git updates the index copy, after which Git realizes that the updated index copy still matches the HEAD
commit and that the file isn't modified after all.
In other words, sometimes fixing the .gitattributes
file is not sufficient, even if the file isn't going to change. Note also that sometimes, the committed version of the file has the wrong line endings if the .gitattributes
entry was missing or incorrect. The work-tree copy may well be right, but until you make a new commit that has the frozen copy built from a correct index copy, Git will—this time correctly—tell you that the file is modified!
TL;DR
You may want .gitattributes
(not ~/.gitattributes
) to contain the line * -text
or *.json -text
. You may want to remove any autocrlf = true
setting from .git/config
or $XDG_CONFIG_HOME/git/config
or $HOME/.config/git/config
or $HOME/.gitconfig
. You might want finer control of additional files, depending on how you and others are using these repositories, on which systems.
Long
For one thing, these lines are wrong:
text=auto
-crlf
The entries in .gitattributes
should have the form of a file name or pattern, followed by white space (typically a tab but blanks are OK too), followed by one or more attributes. Here, the file name / pattern part is missing, though Git does not see it that way: what this tells Git to do is to apply no attributes to all files named text=auto
, and apply no attribute to all files named -crlf
. Since there probably are no files with these funny names, this applies no attributes to no files—so it has no effect.
(You probably wanted * text=auto
and/or * -crlf
, but see below.)
Similarly (but perhaps not the problem), this might be the wrong place:
cat ~/.gitattributes
Git looks for attribute files in several places. The most important one is .gitattributes
in the top level of the repository work-tree, i.e., .gitattributes
, not ~/.gitattributes
. You can have Git consult core.attributesFile
, which you can set to ~/.gitattributes
. Modern Git looks for $XDG_CONFIG_HOME/git/attributes
or $HOME/.config/git/attributes
if core.attributesFile
is not set, so unless you configured your global core.attributesFile
setting, it won't be looking in $HOME/.gitattributes
.
Opinion: core.autocrlf
is not a good idea
cat .gitconfig
[core]
autocrlf = true
If this was from your home directory, this may be the source of the problem.
Setting core.autocrlf
to true
acts this way, as described in the git config
documentation:
Setting this variable to "true" is the same as setting the
text
attribute to "auto" on all files andcore.eol
to "crlf". Set to
true if you want to have CRLF line endings in your working
directory and the repository has LF line endings. This variable can
be set to input, in which case no output conversion is performed.
In general, I dislike text=auto
, because it means Git has to guess whether your file is text. Will Git guess wrong? Sometimes, yes, it will. If you are on Linux, where the end-of-line conversion defaults to do nothing, this is harmless, but on Windows and MacOS, it's not so harmless. Here, a .gitattributes
file listing specifically which line endings go in which files is useful.
It's worth looking at the core.eol
description as well, since you mentioned setting core.autocrlf
to false
:
Sets the line ending type to use in the working directory for files
that have the text property set whencore.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.
I have tried
autocrlf
withauto
andfalse
, too.
The only valid settings are true
, false
, and input
. Well, technically, setting it to 1
, 0
, and -1
respectively also work, as Git will try to convert it as an integer if it does not match one of these words. Setting it to the literal string auto
makes Git treat it as an integer, which converts to the value 0
, and hence means false
. That's the default, in which case core.eol
comes into play provided there are no overrides in .gitattributes
files.
Much of the remaining things to know are buried in that gitattributes
documentation, except for a few key items that are not properly described anywhere. These require a clear definition of Git's index and work-tree.
Commits, the index, and the work-tree
The first thing to realize is that any committed file is frozen into whatever data is in the committed form of that file, forever that way in that particular commit. That is, each commit holds a snapshot of every file—well, every file that's in that commit, but that seems a bit redundant—in the form it had in the index when whoever made the snapshot, made the snapshot (by running git commit
).
If a committed file has a line that ends with CR-then-LF, followed by a second line that ends with CR-without-LF, followed by a third line that ends with LF-without-CR, that committed version of that file always has that form. There is nothing anyone can do about this. If you have that commit, you have that file, with those three line endings in that order. You can choose to check it out, or not; but whatever you do, or don't do, that file exists in that form in that commit.
But wait! The act of checking out some file—or a commit full of files, for that matter—does not mean put that file into the work-tree as-is. Let's consider a file such as x.json
. First, the file has to go through your index. Git's index is a special data structure (stored in one big flat file, usually, although there are some optimizations that use more than one file sometimes: the flat file has various index slots, found partly by names like x.json
, along with linkages to the internal data, which is really stored elsewhere: the index copy is actually just a pointer to the real data). The index has two more names, reflecting, perhaps, its importance in Git, or perhaps that the name index leaves something to be desired: it's also called the staging area, or sometimes the cache, depending on who / what part of Git is doing the calling.
In any case, the x.json
inside the commit is frozen, as we just mentioned, but also is in a special, compressed (sometimes highly compressed) Git-only format. Git first copies that x.json
to your index, unfreezing the file, but keeping it in the special Git-only format. Since the copy in your index currently matches the copy in the commit, it has the same line-endings. But since it's not frozen, you can change the line endings, if you like. Before we get there, let's look at the last step of checking out x.json
.
The Git-only file is useless to anything other than Git. So, Git copies the unfrozen, Git-only file from the index to your work-tree, creating an actual, ordinary-format, x.json
file. It's this last step that does the first pass of end-of-line manipulation, based on:
- whether the file has been detected as "text"—a non-text file is never manipulated this way—and
- the
eol
setting, if there is any, from the.gitattributes
file or implied bycore.eol
orcore.autocrlf
, more or less in that order.
You can, of course, also copy from the work-tree to the index. At this time, Git will once again do end-of-line manipulation, based on those same two items, plus the input
setting, which tells Git to do end-of-line conversions even if the output pass didn't.
The conversions
There are actually two possible conversions. The first, and most common, have to do with carriage-return and line-feed line endings. The second, which is new since commit 107642fe2661 (first appearing in Git 2.18.0), has to do with Unicode encodings.
CRLF conversions
While copying from index to work-tree, Git will replace LF-only line endings with CRLF line endings if you have told it to do so. It will not replace CR-LF with LF; it only does LF-to-CRLF here.
While copying from work-tree to index, Git will replace CRLF line endings with LF-only line endings if you have told it to do so. It will not replace LF with CRLF; it does onyl CRLF-to-LF here. There is some extra magic in modern Git: if the index copy of the file has any carriage-return (r
or ^M
characters), Git inhibits conversion unless it's doing a "renormalize" operation (git add --renormalize
, plus a merge or cherry-pick with the renormalize flag turned on).
Unicode conversions
As I mentioned, these were new in Git 2.18 (and are still new to me). Git will now re-encode UTF-16 files into UTF-8, and vice versa, if you tell it to, through a .gitattributes
entry. As with the end of line conversions, these are directional: git add
, which copies from work-tree to index, prepares the file for storage inside Git and will convert to UTF-8, while git checkout
or git checkout-index
(or various additional cases like git reset --hard
), which copies from index to work-tree, will convert from UTF-8.
Additional re-encodings may be available, based on whatever iconv --list
prints. This, too, is described in the gitattributes documentation.
Changing text=
and conversion-bypassing issues
The default for Git is to guess whether some file is text or not. If a file is text, it gets converted; if not, Git treats it as sacrosanct: whatever is in the index goes into the work-tree, and whatever is in the work-tree goes into the index. So * -text
tells Git: never touch any file at all, and there are no issues. But what if Git was previously thinking * text=auto
?
The answer is to this question is: this does not work well. Bad things happen! Except for git add --renormalize
, Git will believe that the index and work-tree match—and hence not bother to copy one way or the other, if the file was recently extracted or added.
That is, in particular, watch out for this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git add file.ext # use whatever's in the work-tree
and this:
$ git checkout branch
$ echo '* -text' > .gitattributes # avoid conversions
$ git checkout -- file.ext # see what's actually in the commit
The git add
or git checkout
step here may do nothing on the assumption that the work-tree and index already match, even if they only used to match based on the previous .gitattributes
setting (or lack thereof). You can force the extraction, or the adding, by modifying (or even removing, if you're going to re-extract) the file, so that the work-tree copy is now clearly different. But in general, it's annoying and tricky to get a .gitattributes
change to take effect, because Git doesn't realize that modifying .gitattributes
can change the conversions Git will perform.
For that matter, the same is true of core.eol
and core.autocrlf
: changing these configuration items changes the conversion Git might perform, yet Git is not aware that the index and work-tree may now be out of sync with each other. The index's cache aspect works against you here.
Finally, note that Git will tell you that some file is or would be modified if Git sees that git add
-ing the file would change the line endings. Sometimes Git guesses wrong, especially after altering .gitattributes
entries so that files that were classified as text are now classified as binary, or vice versa. In these cases, you have to git add
the file anyway, so that Git updates the index copy, after which Git realizes that the updated index copy still matches the HEAD
commit and that the file isn't modified after all.
In other words, sometimes fixing the .gitattributes
file is not sufficient, even if the file isn't going to change. Note also that sometimes, the committed version of the file has the wrong line endings if the .gitattributes
entry was missing or incorrect. The work-tree copy may well be right, but until you make a new commit that has the frozen copy built from a correct index copy, Git will—this time correctly—tell you that the file is modified!
answered Nov 21 '18 at 3:16
torektorek
193k18239321
193k18239321
Regarding the UTF-8 conversion: stackoverflow.com/a/50435869/6309
– VonC
Nov 21 '18 at 5:46
Thanks torek will try and get back
– kgunjikar
Nov 21 '18 at 18:55
add a comment |
Regarding the UTF-8 conversion: stackoverflow.com/a/50435869/6309
– VonC
Nov 21 '18 at 5:46
Thanks torek will try and get back
– kgunjikar
Nov 21 '18 at 18:55
Regarding the UTF-8 conversion: stackoverflow.com/a/50435869/6309
– VonC
Nov 21 '18 at 5:46
Regarding the UTF-8 conversion: stackoverflow.com/a/50435869/6309
– VonC
Nov 21 '18 at 5:46
Thanks torek will try and get back
– kgunjikar
Nov 21 '18 at 18:55
Thanks torek will try and get back
– kgunjikar
Nov 21 '18 at 18:55
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53404150%2fgit-checkout-branch-ends-up-modifying-files%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I think you are on the right track, and the problem is the line endings.
– Tim Biegeleisen
Nov 21 '18 at 1:43
Is it ok to ask git to not do anything related to ELF on the files? Then try adding this to
.git/info/attributes
:* -text
. That is telling git to use the files as they are on the repo. Can also be set on a .gitattributes file on the working tree.– eftshift0
Nov 21 '18 at 1:43
Didn't help. Same problem, json files are modified.
– kgunjikar
Nov 21 '18 at 2:20