Change case of a file on Windows? [duplicate] - windows

This question already has answers here:
How to make git ignore changes in case?
(6 answers)
Closed 10 months ago.
There are a couple of files in our git-controlled codebase that I'd like to rename. Specifically, I just want to change the case of the file, so that sourceCode.java becomes SourceCode.java, for example. The catch: I'm on a Windows box, and the filesystem thinks those are the same file name.
How can I get Windows and Git to recognize that change and check it in?
The change of the file name should not be ignored, but committed to git.

To rename the file you can use the standard git mv command.
Since Windows treats files with only changes in case as identical, you have to pass the -f option to force a rename:
git mv -f name.java Name.java
If instead you want to ignore case changes, have a look at the question
How to make git ignore changes in case?.

If you are on a FAT file system your only choice is to do a two stage rename:
Rename sourceCode.java to anything.you.like
Rename anything.you.like to SourceCode.java
Back in the days when we used Perforce we had exactly this problem and this was the only solution we could come up with.

The following steps allowed me to change the case on Windows:
Add ignorecase = false to [core] in .git/config;
Move the files you are going to rename out of your project directory;
Add the deletes to the index;
Move all files back to their original location and change the case of the files and/or directories;
Add all "new" files to the index;
Remove ignorecase = false added at the first step.
This way you have a single commit that contains the rename and it makes it easy to change e.g. an entire directory.

In my opinion one simple way is missing. You can do this for a single file, a specific directory or even the whole repository. Just rename your files in any way you like before and than execute these commands:
git rm --cached <file name or directory>
git add <file name or directory>
If you want to affect also the sub-directories, you have to use the -r flag:
git rm -r --cached <directory>
git add <directory>

Be careful. Doing this can lead to changes that are impossible to merge. Git gets confused when merging on Windows because it can't decide whether the old Uppercase name and the new lowercase name are the same file or not (for Git they are not, but for the filesystem they are). To merge you have to do some manual workaround like deleting the files before merging.
See Git rebase issue with files of same name but different case
I'm not sure if this issue is worse than having an unconventionally-named file in your project for ever and ever, but it is worth knowing about if there are lots of users with lots of branches which will all need to be merged eventually.

With NTFS (or FAT), a single git mv command does not solve the problem.
This question shows a technique that works:
git mv and only change case of directory

Related

Git - rename files with illegal characters

Our team works on repository which includes one directory where there are files with pipe character "|". I'm the only one on Windows so pipe character is illegal for files names.
Is there a way to rename files in the directory "extra" from ex. "2021|08|05" to "2021\08\05" when I make "git pull"?
Is there a way to rename files in the directory "extra" from ex. "2021\08\05" to "2021|08|05" when I make "git push"?
No.
No.
These are the right answers to the question you've asked, but the trick is, you've asked the wrong question. Alas, the answer to the right question is: "yes, but it's a horrible solution". The question is: Is there a solution to the problem of bad / invalid characters in file names stored in Git commits?
In the early days of Git, when it was a collection of shell scripts that only a few people could use successfully, 😀 one would obtain new commits from elsewhere with git fetch, then read these commits into Git's index with git read-tree.
Git's index, which Git also calls the staging area or sometimes the cache, can hold these file names. In fact, even on Windows, Git's index can hold files named aux.h, which Windows won't let you create. The index has no folders either: it just has files with names with embedded (forward) slashes, such as path/to/file. Git can hold two different files, one named README and one named readme, in its index. WIndows can't have two different files whose name only differs in case.
So, Git's index / staging-area can hold these files just fine. The problem comes when you go to work with the files. Files that are in Git's index, are stored there in a special Git-only format, as what Git calls a blob object. You cannot read or write a blob object directly. You have to use yet more Git commands to do this. It's terribly inconvenient.
To use Git conveniently, then, we normally don't use all the individual one-step-at-a-time internal Git operations: we use some sort of higher level, user oriented command, like git checkout. We check out an entire commit: Git will find all the files that are stored in that commit, read them into Git's index, and copy out and expand all the internal Git-only blob objects into ordinary files, with ordinary file names.
This step—copying files out of Git's index, to make them usable—is where things go wrong, on Windows. The file's name in Git's index is, say, path/to/2021|08|05. Git recognizes that path/to/ has to be turned into two folders, path\ and path\to\, on Windows, so that Git can create a a file in the second folder. Unfortunately, Git has no way to remap the 2021|08|05 part. That part is going to stay 2021|08|05, and as you have seen, Git can't create a file with that name: the OS just says "no".
What you can do, at this point, is drop down to those lower-level commands. You can run:
git rev-parse :path/to/2021|08|05
perhaps with quotes if needed, depending on your shell:
git rev-parse ":path/to/2021|08|05"
This git rev-parse command will show the blob hash ID for the file. You can then access the file's contents with:
git cat-file -p <hash>
which prints those contents to the standard output. If your shell supports redirection, you can then redirect the output to a file whose name is your choice. This lets you see and use the file's contents.
The git cat-file -p command can take the index path name directly, so:
git cat-file -p ":path/to/2021|08|05" > path/to/2021-08-05
is a way to extract the file to a usable name.
Unfortunately, git add—which is how you would normally update the file—will insist on using the name you gave the file in the file system. Once again, you must fall back on internal Git plumbing commands to work around this. If you need to update that particular file, for instance, you would:
run git hash-object -w -t blob path/to/2021-08-05 to turn the updated file's data into an internal Git object;
run git update-index with arguments to make Git update the entry for path/to/2021|08|05 using the hash ID obtained in step 1.
Once all of this is done, you can go back to normal Git commands, because git commit makes a new commit from what's in Git's index / staging-area.
The (rather large) drawback here is that you cannot use a lot of normal everyday Git commands:
git pull is often a no-go because it runs git rebase or git merge, both of which need to use your working tree (OS-level files). Run git fetch first, then do as much manual work as needed.
git checkout will fail: you can use it, but then you must manually do something about each of the bad file names that are now in Git's index.
git diff will show differences that include deleting the files with the bad names, and git status will show the adjusted-name files as untracked files (because they are).
git add of any changes you need to make to these files is also a no-go; use git hash-object -w and git update-index instead.
git rebase and git merge become difficult. You probably can deal with them as in steps 2 and 4, but that's painful at best.

Folder capitalization not changing on branch switch

I'm working on a python project and want to rename a (package) folder to small letters, let's say from Myackage to mypackage. As git is case-sensitive and Windows is not, I followed the solutions taken from here and espacially here.
My procedure was as follows:
git mv Mypackage tmp
git mv tmp mypackage
git commit -m "Change capitalization of package name"
This changes the folder Myackage to mypackage with success (for both, git and Windows). But if I switch to another branch, I expect the folder to change back to Mypackage (with capital letter!), as it was before. Background is, that all the imports of the package are also case-sensitve in python and i need this renamng acompanied with adaptions of the imports.
I've tried both, core.ignorecase set to true and false, but no matter what I try, if I checkout an older branch, the folder remains in form of small letters (mypackage) and I run into issues within python.
UPDATE:
I've set up a small example with only one Folder and one file and could succesfully change the capitalization of the folder. It also shows the desired behaviour, that upon branch switch the capitalization of the folder in Windows changes, yet still this won't work for my python project.
Could it be, that, e.g., submodules, play a role here?
UPDATE 2:
I've checked the case sensitivity attribute for both cases via:
fsutil.exe file queryCaseSensitiveInfo .
Both folders claim, that case-sensitivity is deactivated. Still for one project folder name capitalization changes, but for the other folder not.
The attribute case sensitivity is available on Windows 10 but after April 2018 Update and only affect the specific folder to which you apply it. It isn’t automatically inherited by that folder’s subfolders. However, if you use WSL to create folders it's enabled by default and available in that way to Windows. [1]
Although you can use the Git Unite [2] tool to match the case of the current folders with the git index.
If you use the rename approach, try using it with git commands like in "Rename files and folders with git"[3]
git mv foldername tempname && git mv tempname folderName
I found a way to reproduce your behavior :
if my CaSeD folder contains some extra files (untracked files for example), git will not change the case of my folder name when I jump between commits.
Is this the case in your setup ?
If this is your issue : you could go with a post-checkout hook, which forcibly renames the folders according to what is stored in HEAD after a checkout.
One way to get the full list of paths to directories from commit HEAD is :
git ls-tree --name-only -d -r HEAD
If you match this list with a similar list extracted from your local file system (ls -r ? find . -type d ? some python function from os.* ?), you can spot what folders need to be recapitalized.

Git on Windows capitalized file names on origin, lower case locally

We are forced to work on Windows at work, and I have lets say problem, strange situation. We have github repository, inside which we have one directory with name Something (with capitalized first letter 'S'), but in my local I see this directory with name something (note lower case 's'), git status shows that working directory is clean, even if I change this directory locally to, for example SoMeThInG git says that nothing changed. I suspect that Windows is here a problem, as it is case insensitive. Is there possibility to change this directory name from Windows level? Or maybe how to force git bash to be case sensitive?
Update
I've changed that files from mine virtual fedora, but this is just a workaround, the question remains unanswered, how to do it properly on Windows?
On case-insensitive file systems, Git will not detect changes just in casing. However, when committing files, the actual casing is still being reflected in the way it was added to the index.
So a git add file and git add FILE will both work for a file that is named file in any kind of casing (e.g. FiLe or fIlE), but each command will actually stage that exact name into the repository. So git add file will make the name be case-sensitive file and git add FILE will make the name case-sensitive FILE.
That’s why you should try to always use your command line auto completion for file names, so you don’t accidentally add files with a different casing than they actually are. Or use commands that stage the files automatically, e.g. git add ., since that will also use the actual casing.
However, since Git will not detect casing changes, once a file has been added with a particular casing, that casing will be used until you explicitly change it. That’s why it’s possible to have files in a folder src/readme.md and SRC/license.txt that are both physically in the same location on your file system, but are represented using incompatible paths inside of Git. So you should be careful here.
That all being said, you can fix the casing later. But to do that, you need to make the change using Git instead of the file system, as Git is case sensitive while the file system isn’t. So commands like git mv will work. Same as a combination of git rm --cached and git add.
For example, to fix the above situation of the src/SRC directory, one could do (assuming the correct name of the folder should be Src):
git mv src/readme.md Src/readme.md
# or
git rm --cached SRC/license.txt
git add Src/license.txt
You can also fix the casing for every file by removing everything from the index, and then adding it back:
git rm --cached -r .
git add .
That should stage all renames to the correct file casing.

.gitignore is not working on Windows

I used to develop on linux environment, but now I need to write a git project on "windows" os. I need to ignore a directory from where the git is initiated.
like folder/.git/, now I need to ignore /folder/project/bin/*. So I have added .gitignore file on folder/.gitignore using both text pad and Visual studio.
.gitignore file contents are
/project/bin/*
Now, I expect git status should not show /project/bin folder.
Note, I have tried other ways too, but it seems that git is considering the .gitignore file as a general text file or so.
Just a guess, but I suspect that the leading forward-slash is throwing something off. I'm not sure about the trailing star either (as I reference my own .gitignore files).
Your .gitignore should be this:
project/bin/
I frustratingly found the same issue. I couldn't seem to ignore folders, but files were OK. My issue started with folders that contain spaces. All the combinations I could find after a searching (full directory path, trying to ignore case, escaping the spaces, etc) didn't resolve it.
I had to remove spaces in directory names, then I was able to ignore files.
I tried it on WSL (Windows sub-system for Linux - git was already installed), no problems.
Then reading more, I realised that if files are previously tracked, then the .gitignore won't work. I tested it by deleting all the files in the directory, then adding the directory to .gitignore path/to/my_files/*.csv (in my case I'm ignoring data in .csv files) and doing a commit then push (good job this is just dev).
After that worked, I was able to use git rm -r --cached ./path/to/my_files/ to un track all the files in another directory rather than cutting and pasting them back (Thanks to this answer)
So:
Un-track all the files
Add the directory in .gitignore (In windows it wasn't case sensitive)
add/commit/push as normal.

Programmatically overwrite a specific local file with remote file on every git pull

I have an XML file that we consider binary in git. This file is externally modified and committed.
I don't care about who edited it and what's new in the file. I just want to have the latest file version at every pull. At this time, at every git pull I have a merge conflict.
I just want that this file is overwritten on every git pull, without manually doing stuff like git fetch/checkout/reset every time I have to sync my repo.
Careful: I want to overwrite just that file, not every file.
Thanks
I thought you could use Git Hooks, but I don't see one running before a pull...
A possible workaround would be to make a script to delete this file and chain with the needed git pull...
This answer shows how to always select the local version for conflicted merges on a specific file. However, midway through the answer, the author describes also how to always use the remote version.
Essentially, you have to use git attributes to specify a specific merge driver for that specific file, with:
echo binaryfile.xml merge=keepTheirs > dir/with/binary/file/.gitattributes
git config merge.keepTheirs.name "always keep their file during merge"
git config merge.keepTheirs.driver "keepTheirs.sh %O %A %B"
git add -A
git commit -m "commit file for git attributes"
and then create keepTheirs.sh in your $PATH:
cp -f "$3" "$2"
exit 0
Please refer to that answer for a detailed explanation.
If the changes to your files are not actual changes, you should not submit them. This will clutter your version history and cause numerous problems.
From your statement I’m not quite sure which is the case, but there are 2 possibilities:
The file in question is a local storage file, the contents of which are not relevant for your actual sourcecode. In this case the file should be part of your .gitignore.
This file is actually part of your source and will thus have relevant changes in the future. By setting up the merge settings like you are planning to do, you will cause trouble once this file actually changes. Because merges will then be destructive.
In this case the solution is a little bit more complicated (apart from getting a fix for the crappy tool that changes stuff it doesn’t actually change …). What you are probably looking for is the assume unchanged functionality of git. You can access it with this command:
git update-index --assume-unchanged <file>
git docu (git help update-index):
You can set "assume unchanged" bit to
paths you have not changed to cause git not to do this check. Note that setting this bit on a path does not mean git will check the
contents of the file to see if it has changed — it makes git to omit any checking and assume it has not changed. When you make changes
to working tree files, you have to explicitly tell git about it by dropping "assume unchanged" bit, either before or after you modify
them.

Resources