Why Is Git Creating Read-Only (444) Files

Why is git creating read-only (444) files?

Those files are part of the object database, which really is read-only. No matter what you do with Git, you can't change the contents of a specific object once it has been created.

Note that if you back out a commit and create a new one in its place, you'll be creating a new object with a new identifier and new contents. Git will eventually perform its garbage collection to remove the old, unreferenced object(s).

`.git/objects/` directory contents: make git set the write permission instead of 'read-only'

My answer below isn't so much a solution for your problem, just more explicit information about why and where the git maintainers made this choice.

See: Why is git creating read-only (444) files? That stack overflow question references the relevant source code and documentation.

I couldn't find any relevant setting(s).

This git behavior causes an issue for me with a personal, local, offline, bare git repo. I need everything in the .git dir to be user-writable because I use a custom bitrot detector, and that bitrot detector works by writing a checksum to extended file attributes for later verification. Sure would be nice if there were a git configuration option around this, but I'm guessing writable index files is an edge case and the git maintainers might not even accept a patch to add one. Or they might.


I'm sure you're past all the rest below, but I'll include it for completeness and for anyone else who wants these git internal files to have more open permissions.

I imagine the git maintainers use 0444 perms for these files and I basically agree with their decision, so I use a fairly brute-force workaround. Before I run my bitrot detector, I give myself write permission:

find .git/objects/ -perm /u+w | xargs -r chmod u+w

and I remove it afterwards with:

find .git/objects/ -perm /u+w | xargs -r chmod u-w

Since git is "special" with its read-only files, maybe alias gitnuke (or make it a function) to rm -rf or do a find workaround like I did above. Side note: I usually use rm -rf to recursively delete any directory tree anyway, so I've never run into the problem you face.

Git: Track permission change 777 to 444

You could remove the file from your repository and replace it with the file that you changed.

What file permissions should the contents of $GIT_DIR have?

Directories should have 755 permission; files should have 644 permission.

That's a pretty good rule of thumb unless you expect members of your group to make changes to your repository.

Having said that, in one of my repositories, the files under .git/objects/* have 444 (readonly) permission for everyone. Other files are 644 as suggested.

This script, run in the top-level directory just above the .git repository would fix the permissions:

 find .git -type d | xargs chmod 755
find .git/objects -type f | xargs chmod 444
find .git -type f | grep -v /objects/ | xargs chmod 644

I started with -print0 for the first two find commands and xargs -0 to allow for the remote possibility of spaces in file names. But the grep -v in the third command would be difficult to manage with the -print0 format - so I omitted the space-safe notation from all the commands, knowing that git does not create files with spacing in names under the .git directory.

How to restore the permissions of files and directories within git if they have been modified?

Git keeps track of filepermission and exposes permission changes when creating patches using git diff -p. So all we need is:

  1. create a reverse patch
  2. include only the permission changes
  3. apply the patch to our working copy

As a one-liner:

git diff -p -R --no-ext-diff --no-color \
| grep -E "^(diff|(old|new) mode)" --color=never \
| git apply

you can also add it as an alias to your git config...

git config --global --add alias.permission-reset '!git diff -p -R --no-ext-diff --no-color | grep -E "^(diff|(old|new) mode)" --color=never | git apply'

...and you can invoke it via:

git permission-reset

Note, if you shell is bash, make sure to use ' instead of " quotes around the !git, otherwise it gets substituted with the last git command you ran.

Thx to @Mixologic for pointing out that by simply using -R on git diff, the cumbersome sed command is no longer required.

Git: Track permission change 777 to 444

You could remove the file from your repository and replace it with the file that you changed.

Diagnosing a git error -- fatal: adding files failed

The error message:

error: insufficient permission for adding an object
to repository database .git/objects

is close, but is potentially missing a few extra details. Git's object store (a simple key-value database) is currently implemented as a directory (or folder, if you prefer that term) named .git/objects that contains numerous subdirectories (subfolders). Each directory must allow writes by the current user and/or group, with permissions being determined and managed by the operating system (not by Git itself, except as described below).

On a Unix or Linux like system (as you appear to be), permissions to read and write individual files are controlled by the file's permissions, while permissions to create new files or delete existing files (or create and/or delete subdirectories) are controlled by both the execute and write permissions bits of the various directories.1

Git will try to create new directories named .git/objects/xx where the xx represents any two hexadecimal digits if and when Git needs to add an object whose hash ID begins with those two digits. It will then try to create a new file in that directory where the file's name is the remainder of the hash ID (i.e., after stripping off the first two hexadecimal digits).

Besides the xx directories, there's a directory named pack that will be created as soon as there are any pack files (these contain packed objects, while the two-hex-digit directories contain loose objects; the difference is that the packed objects are more-compressed, and also try to work around bad performance of file systems with many loose objects). The pack directory and files within it behave the same way as the loose object directories.

When creating a new directory, Git calls the OS's mkdir with permissions 0777. Your umask, which will generally be either 002 or 022, will clear the write permission for others (result 0775) or group-and-others (result 0755) respectively. Git uses the core.sharedDirectory setting, if it exists, to set its umask relatively early on, in case you need these to be group-writable: this is normally only the case for a shared repository. Setting core.sharedRepository to true or group causes all of these to be group-writable.

Because you appear to be using Docker (modified: .devcontainer/Dockerfile) and Docker can result in UID and/or GID mapping, you might need shared-repository mode, even if you're working alone, and even that might not suffice. See appropriate Docker documentation to find out whether you're doing ID mapping and if so what you might need to do about that.

Otherwise—if you're not trying to share the .git repository across a docker mount or if you're doing this in such a way that ID mapping is irrelevant—just make sure you have not incorrectly set up the existing directories within .git/objects. In general, they should be:

  • files: mode 0444; Git writes such files once, and then does not change them, so they're deliberately read-only
  • directories: mode 0755 or mode 0775 depending on core.sharedRepository

You can set them to mode 0777, and set core.sharedRepository to world or all or everybody, if you have both UID and GID mapping going on in such a way that this is necessary.

Use chown and/or chmod recursively as shown in Yigit Yasar's answer but with the correct options to adjust the ownership and/or permissions of files. Note that you want:

chown -R user:group

(not group:user) if you need to change these, and:

chmod -R g=u

(or chmod -R go-u) to add group (or group and other) write and/or execute permissions to match the user permissions if you need group and/or world writable settings. In general, don't make the permissions any looser than necessary—this isn't because Git will break, but just a matter of general "permissions hygiene" where we don't want our systems to be too easy to attack.


1Specifically, write permission on a directory implies that you can create or delete a name within that directory. Execute permssion on a directory implies that you can use that directory to look up files. Read permission just means you can see what files are in it—you can still use the files if you know their names, even if you can't read it, as long as you can "execute" the directory. (However, some network file systems require both read and execute permissions.)

How to make sure others people cannot edit my git notes?

I don't think you really can protect published git notes.

What you can do is put them in an explicit namespace, as mentioned in here:

I think for "typical usage" one stores others' notes in a different place anyways, e.g. I store Thomas' list-notes in refs/remotes/trast/notes/ so that they don't interfer with my own notes.

If in the same namespace, then they can be merged:

git checkout refs/notes/commits
git fetch origin refs/notes/commits
git merge FETCH_HEAD
git update-ref refs/notes/commits HEAD
git checkout master

But that would change their content.



Related Topics



Leave a reply



Submit