Switching GIT Branches randomly changes filename case - windows

I am having an odd issue with GIT.
I have a git repo for a small project I'm working on. There are no remotes, this is just for my own work. Up until a few days ago, my development didn't even warrant any branches.
I have finally needed to make a branch for some experimental code. When I was done, I simply checked-out the master branch to go back to where I was. And this is where problems started.
There are six files that change between the new branch and the master branch. Every time I switch / checkout either branch (switching between the two) most of those six file names change case.
For example, if the file was supposed to be someCode.py:
Sometimes the file name switches to somecode.py (Incorrect)
Sometimes the file name switches to someCode.py (Correct)
It doesn't matter which branch I pick, the result is different every time. And which files end up with which CaSe is different every time.
I suspected the GIT plugin for VSCode I was using (GIT Graph) at first, but it also happens with the included GIT GUI, and even happens if I use "git switch" from the command line.
I read about the core.ignorecase setting; it was set to True. I tried setting it to False and the problem persists.
Does anyone have any ideas what's going on?
Thank you
Details:
GIT 2.30.0.windows.2
Windows 10
Files are on a mapped network drive
File system is NTFS

Natively, Git is case sensitive so you could have two different files named filename.txt and FileName.txt. On some operating systems (e.g. Linux) that works fine, and on others (Windows) it does not. Note the same thing goes for branch names too; you can't have two different branches differing by case only on Windows. The reason for this is branch names are stored as a file on your drive.
Most likely your problem is that the files in question have different names on the two branches, but differ by case only. If so, pick which one you want (perhaps master), and on the other branch rename those files to match the casing exactly. If they are actually supposed to be different files, then change their name by more than just casing.
Another possibility is that two (or more) files with names differing by case only exist in the same branch, and switching branches simply causes the issue to come to light. Note it could be the files themselves, or it can be any of the directories in the path to those files could have different casing too. To determine whether this is the problem, navigate to the root of your repo in Git Bash and run this command:
git ls-tree -r HEAD | grep -i [filename-without-path]
If you see two or more files with the same name but different casing, you've identified the problem.

It seems I have resolved the issue - at least to a point.
I created a new branch, manually fixed the file names in Windows Explorer. Now, switching between master and the newBranch does things as expected.
However, if I ever switch to the firstBranch from the question, the issue resurfaces. There seems to be an issue with that branch, and switching in and out of that branch causes the issue.
I still do not have an answer as to why.
EDIT: OK, maybe not. Repo seems to be having this problem again with other branches.

Related

Extra branch displayed in Xcode Source Control Pane

Weird issue here. I merged two branches with different feature sets. I branched from one of the two then merged in the second (using command line). Now in Xcode's source control pane it shows two different branches as current. It's not a big deal but I would like to know how to get rid of the top one (at the very least) and preferably to understand why both would be displayed as current. It makes pushing to remotes a bit of a headache because they have the same app name and so I need to make sure I don't push to the wrong branch. I don't particularly want to have to redo things a different way since the branches had a fair number of merge conflicts.
Figured it out... There were two files that were pointing to the other branch folders in the pbxproj file. This appears to have happened when I merged. Removing the references to these files and pointing to the files in the correct project folder then committing and pushing made the extra branches go away.

Checked out a repo from remote but when I do a git status a file shows up as modified — how to fix?

I am using Windows and Git and I had modified a file. No matter how many times I did a git add and commit, the file kept showing up as modified and i could not for example do a git pull --rebase. I assume I did something wrong and screwed up the local Git repo so I decided to clone the repo from github, into a completely new directory. To my surprise, even in this new directory tree when I do a git status the same file shows up as modified -- it is as if it is somehow modified in the github (remote) repo which does not make sense to me. Moreover, the version of the file in cloned local repo does not have the latest version of the code that i can see when i look at the code on github. How can i fix this? I am concerned that someone else cloning the code will end up with the same problem. (Apparently only I am seeing this problem -- I did not somehow manage to corrupt the github repo which leads me to believe this is a git/windows issue.) As far as what I think I did wrong is when I modified a file and did a git add, i misspelled the directory path by using a lower case letter instead of an uppercase and then adding one file resulted in the other, properly spelled path showing up as modified and vice versa. I don't know if a symlink on windows got created -- the file contents are identical. But one would think cloning (via Eclipse) into a completely new directory tree would make this a non-issue.
I looked through replies but it seems like the basic problem is Window's case insensitivity and this caused some (to me) weird behavior. In particular, I simply could not delete one of the folders -- they were "entangled." So the simple solution was to delete the folder and its contents from unix which is case sensitive. Then I checked out a fresh repo and problems appear to be completely resolved.
You mentioned in a comment that you discovered one commit containing two problematic files: one named Login/Login.tsx and one named login/Login.tsx. This comment is on a related question; see my answer there for a discussion of Git's method of naming files in its index, vs what your OS requires in your working tree.
Your solution—use a Unix or Linux machine, where you get a case-sensitive file system, to repair the situation—is probably the easiest and best way to deal with this. If you can establish a case-sensitive file system on your own machine, that also allows easy dealing with this (see my answer to another related question for a macOS-specific way to make a case-sensitive file system).
Given that what you wanted was simply to delete one of the spellings, though, git rm should allow you do that. In particular git rm --cached login/Login.tsx would drop login/Login.tsx from Git's index, without affecting Login/Login.tsx. This could leave your working tree with an existing login folder, though.
It's important—at all times, really, but especially when working within a situation like this—to realize that Git itself doesn't actually need or use your working tree to make new commits. Each commit contains a full snapshot of every file that Git knows about. These files exist as "copies" in Git's index.1 Hence there are actually three copies of each file:
A frozen version of each file appears in the current commit (whatever that commit's hash ID is).
A "copy" (see footnote 1) of that version appears in Git's index. You can replace this copy with different content, and the read-only copy in the commit doesn't change. You can remove this copy entirely, and the read-only copy still doesn't change. Nothing in any existing commit can or will ever change. The index copy exists precisely so that you can replace it, or remove it, or whatever. In effect, the index—or staging area, if you prefer this term—acts as your proposed next commit. It's merely filled in from a commit.
Finally, there's a regular, ordinary, everyday file. This copy goes into your working tree or work-tree. To put this copy in place, Git must use your OS's file-manipulation facilities. That may require creating folders and files within the folders. If those are case-insensitive, and Git goes to create a Login folder when a login folder exists, or vice versa, the OS will say: nope, sorry, already exists. Git will do its best to accommodate the OS by using the "wrong" case anyway, and will create a file within that wrong-case folder—or perhaps destroy some other work-tree file that has the same name except for case, or whatever.
This last bit, where your work-tree files end up with the wrong names and/or in the wrong folders and/or end up overwriting similar files whose name differs in case somewhere, is a problem for you. It's not a problem for Git, though. Git just keeps using the index copies of each file. The next git commit you run uses whatever is in Git's index. The fact that your work-tree doesn't match is not a problem for Git. It's just a problem for you, because the normal everyday git add command means make the Git index entry for this file match the copy that's in my work-tree, and if that's the wrong copy, well, that's a problem.
In any case, once you have a correct commit in Git as your current commit, and extracted into Git's index, you can do whatever you like to your work-tree, including remove large swaths of it, or rename folders, or whatever. Get it set up however you like, then use git checkout or git restore to re-extract all or part of the current commit to your work-tree. Now that you've eliminated the name-case-issues in Git's commit and index, and cleaned up or removed any problematic files and/or folders in your work-tree, Git can create correct-case folders and/or files as needed. It's the process of getting the correct commit into Git that's painful, except on a case-sensitive file system.
1"Copies" is in quotes here because the files in Git's index—which Git also calls the staging area—are in a special Git-only format that de-duplicates content. When the copies that are in Git's index match the copies that are in some existing commit, Git is really just re-using the existing commit's files. Files with all-new content actually require a new internal blob object, which Git creates as needed; after that, the content will be de-duplicated as usual.

Git is seeing changes I didn't make in xcode

I'm programming in xcode (actually Phone Gap in conjunction with xcode) with git. I'm having a strange issue. When I create two identical branches and try to check out back and fourth between them with out making any changes git is telling me that I need to commit because a change has been made (this same thing is also resulting in merge conflicts). It says the changed file is:
platforms/ios/Butterfli.xcodeproj/project.xcworkspace/xcuserdata/benpearce.xcuserdatad/UserInterfaceState.xcuserstate
Can anyone explain what's going on and how to deal with it.
Yes, .xcworkspaces are simply files that Xcode uses to describe the workspace or projects.
IMHO, There's no need to check these files in at all, unless you share settings with other folks in your projects (and in your case, I suppose other machines that have a user named "benpearce").
In fact, you can safely add them to your .gitignore file.
More info can be seen here

How do I ignore filemode changes in Git after conflict resolution?

I'm seriously about to stop coding and become a carpenter. This problem has had me stressed out for quite some time now and there doesn't seem to be any clear solution, other than forcing non-windows machines to use the file permissions windows seems to inflict.
Let's begin with the scenario. I have 2 development machines, one running Windows7, the other Mac OSX. They both are using Eclipse and EGit, and both have cloned the same project from a remote repo. That's where the similarities end, because my windows machine has a nasty habit of retaining a file mode of 644 (r-xr--r--) on its local repo, while the mode on the Mac defaults to a cool 775 (rwxrwxr--x).
So the problem's obviously the file permissions - GIT reports there are files that have changed due to differences in file modes and not actual content. The solution seemed obvious, run the following commands:
git config core.filemode false
git config --global core.filemode false
...which worked like a charm, except when committing and merging resolved conflicts.
For example, say files A, B and C exist on both the Windows and Mac repos. Next, let's change the file mode for these 3 files on the Mac machine so that the developer can edit them. Now change some of the contents in file A. Because we're ignoring the file modes (thanks to the commands above) only file A will be committed and pushed, ready for the Windows machine to fetch, merge and enjoy...
Now, let's edit file A again on the Mac and on the Windows machines, effectively causing a potential conflict, and let the Windows machine commit and push file A first. Then, when the Mac user commits their changes to file A and fetches the latest changes from the remote repo, a conflict is obviously created.
After resolving the conflict on the Mac machine and adding file A back to their local repo, committing that merge includes the previously ignored files B and C, and thus highlighting my problem! Why are the previously ignored files being included in this merge commit? This doesn't seem to be a Mac / Windows problem exclusively, as this problem can be recreated both ways...
This probably wouldn't matter if there were only 3 files, but this project I'm referring to includes thousands, and all these up and down push and pulls are insane. Am I missing something really obvious? Any help will be greatly appreciated!
So after a long and often frustrating run with trying to get Windows and Mac OS machines to like each other when comparing notes and changes over Git, it seems to just be the functionality of Git itself that's driven me mad, and a better understanding of how to better use Git seems to be the final answer.
Originally, I wanted to know how to continue ignoring file-mode changes (which Windows seemed to love updating with its own idea of what permissions and modes should be) while I was resolving conflicts created from updates to the files from someone else. Short answer: you can't. It's just the way Git works.
So how would you deal with this situation when it came up? Short answer again: use branching.
In my original way of using Git, I was continually working on my master branch on my local repo - bad idea already - so all my work would be committed there and all conflict resolution would need to be handled there too, forcing all files to be compared again and all permissions and file modes to come into question.
With branching, you work on another branch, commit to that branch, pull updates to your master branch, and merge you other branch with your master branch after that. Suddenly no more conflicts on the master from the remote repo, and you're winning!
Commands to do this (creating a branch from currently selected branch):
git branch newbranch
To checkout your new branch:
git checkout newbranch
To merge your new branch with your master branch (after you've committed to your newbranch, switch to the master first):
git checkout master
git merge newbranch
Hope this helps someone! -:)
well tbh, I'm not sure how you arrived at your conclusion since the obvious problem is git is not ignoring the file mode changes. This happens to us here too.
if you set that flag it seems to make no difference in some cases and still uses file modes to determine changed files.
that has to be a bug in git, what else could it be?
possibly your answer does not explain the rationale, therefore I dont think it's the correct answer, it's a workaround.
the correct answer is that git doesnt use filemode when it's told not to, it's obviously ignoring that and doing it anyway.
can you explain otherwise.

How to make "git branch" respect the "core.ignorecase" flag on Linux?

We are using a remote Git repository located on a Linux server at the office. All clients are Windows PCs with Git Extensions installed as a client (running with msysgit).
On a client system, if I try to do the following:
git branch Branch1
git branch branch1
the second command is going to fail, telling me that a branch with that name already exists. That is what I expect, since I set the core.ignorecase to true in git config.
But, if I log onto the Linux system directly and run the same commands, both branches will get created, even if the core.ignorecase flag is set to true.
I would have expected the second command to fail too, since I configured the repository to ignore the case, but either the flag does nothing on Linux systems, or I missed something.
Can anyone help me understand the issue as we plan on migrating our SCM from Source Safe to Git shortly and this issue is scaring us. What would happen if two developers create branches with the same name, but different case, and push the result on the Linux repository?
I would like to add more details over #meagar's answer:
FAT32 / NTFS are case-preserving file systems. That is, if you name a file "Foo.txt", it will be stored as ""Foo.txt". If you save a file as "foo.txt", it will be saved as "foo.txt". But since it is case-insensitive, "Foo.txt" and "foo.txt" are effectively the same and you cannot have both files in the same directory.
In your repository in Windows, if you change the name from "Foo.txt" to "foo.txt", and if you do not git to show that as a change , you can set core.ignorecase configuration to true and git will not see that as a change. If you set it to false it will. ( But because of the nature of the filesystem and git, it will seem like a new file foo.txt was added, adding to confusion on Windows).
That's the purpose of the core.ignorecase
Coming to the branch. Branches are just pointers to a commit. These pointers are just files. These files are stored in .git/refs/heads. When you create a branch, say, bar, a file named .git/refs/head/bar is created. Now, in Linux, when you create a branch named Bar, it can go ahead and create a file .git/refs/head/Bar and hence allows the branch creation. But on Windows, you cannot create .git/refs/head/Bar and hence you will not be able to create the Bar branch when bar exists. Realize that core.ignorecase is to do with files in your repository - your code base - and has no influence over the metadata files of git.
So you will have to live with and adjust to the fact that in Linux, you can create branches with same names, differing in case, but in Windows, you cannot.
How to make “git branch” respect the “core.ignorecase” flag on Linux?
You can't, because the flag doesn't do what you think it does.
core.ignorecase
If true, this option enables various workarounds to enable git to work better on filesystems that are not case sensitive, like FAT.
core.ignorecase has nothing to do with the behaviour you're describing, it is unrelated to the case-sensitivity of branch names. Git being unable to tell the difference between Branch1 and branch1 on Windows isn't related to whether core.ignorecase is set, it would behave the same way regardless.
... but either the flag does nothing on Linux systems, or I missed something.
You didn't miss anything. The flag does nothing on Linux systems. core.ignorecase isn't for making operating systems case-insensitive, it's for allowing Git to work on broken case-insensitive operating systems. Linux is fundamentally case-sensitive, and so is Git. You should be trying to work with this, and mandating that your developers use lower-case for their branch names. You should not be trying to make Git emulate broken behaviour from other operating systems.
What would happen if two developers create branches with the same name, but different case, and push the result on the Linux repository?
Then you would have two completely different branches, one in lower-case and one which was wrongly named, and somebody would be forced to merge and discard their wrong branch. This is something you'll be doing routinely anyways, as a side-effect of using Git.

Resources