Reputation: 33
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.
Upvotes: 2
Views: 1721
Reputation: 487725
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:
git hash-object -w -t blob path/to/2021-08-05
to turn the updated file's data into an internal Git object;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.Upvotes: 5