21

I have a problem with Git. I searched for a solution in Google and in StackOverflow but nothing helps.

The problem is that every time git updates some file in the working directory (when I checkout branches or I merge a branch, etc.) then the file permissions are changed such that the "writable to group" flag is added. And my apache shows "Error 500" for the file if it is writable to group.

Example: I have a file index.php. Permissions are "-rwxr-xr-x". Current (active) branch is master. This file was changed in the branch "develop". I execute "git checkout develop" and the file index.php gets permissions "-rwxrwxr-x" (writable to group is added). And my site stops working. As apache doesn't allow this flag in php files (I don't know why but I can not change this).

Every time when I execute "git checkout develop" I need to execute also "chmod g-w index.php". I don't like to execute two commands (and sometimes I forget to execute this and my site doesn't work).

What can I do to solve this problem? I think this is something related to umask. I did some tricks I found on web, but nothing works.

Thanks.

bryce
  • 459
  • 5
  • 22
Roman Gelembjuk
  • 1,501
  • 2
  • 18
  • 44
  • 1
    Is there any reason you can't just set your umask appropriately (i.e. to 0022 or similar)? – CB Bailey Jul 20 '12 at 07:18
  • How to do this? To to set umask? Is this system change or only git change? If system then i can not change this – Roman Gelembjuk Jul 20 '12 at 07:28
  • 1
    Just run `umask 0022`. To set it for interactive sessions put this command in your `.bashrc` or shell's equivalent. To set it always, but it in your `.profile`. – CB Bailey Jul 20 '12 at 07:31
  • I don't want to change any system settings. The site runs with this user. I worry this can affect on something else. Is there a way to solve the problem with only changes in git settings? I know there are post update hooks in git. But i can not find how to solve this using hooks. – Roman Gelembjuk Jul 20 '12 at 08:06
  • 9
    It's not a git issue, it's a umask issue; if the site runs as this user and only works if this user creates file which are not group writeable then surely you _want_ to change this user's umask to be 0022. (`.profile` are _user_ settings, not system settings) – CB Bailey Jul 20 '12 at 08:11

4 Answers4

11

Quick answer is this shell function to be put in your ~/.profile. An explanation follows.

git(){(umask 0022; command git "$@")}

A umask is property of a process. It is inherited from the parent process and can be changed from inside later. The command to change umask is usually named umask too.

Git has no configuration option for setting its umask, it does not change its umask after it is executed. You have to set Git's umask from outside, let it be inherited from parent process (usually a shell).

Mmm, you seem to dislike the idea that anything except git has changed umask. So let's change it just when executing git.

When a shell executes a line, it takes the first word on the line and tries to find a function of that name. Only if there is none, it tries to locate a command of that name in PATH. The function I've written above is named git, therefore any direct invocation of git now executes it instead of the git command.

The function executes a subshell, changes its umask and executes the git command from inside the subshell. After Git finishes its work, the subshell also exits and the original shell instance will still have the original umask.

However, the function also shows how to bypass itself. If you call git via command git or even /usr/bin/git, the function won't be called. For any decent use this is good enough, though.

Palec
  • 10,298
  • 7
  • 52
  • 116
9

It is kind of dangerous to allow file execution as a binary. Anyway I solved the problem with umask. My post-receive script looks like:

!/bin/sh
umask 002
GIT_WORK_TREE=/var/www/site git checkout -f

So, file permissions set to 664 and directory permissions set to 775, which suits me perfectly.

P.S. Setting umask in a .profile file of git user has no effect, and I don't understand why, please comment out if you know why this happens.

zogby
  • 483
  • 4
  • 15
  • `umask 0022` would make more sense for a regular webserver as only the owner will have write permission: Resulting in `file permission` 644 and `directory permissions` 755 – Laszlo Schürg Mar 16 '18 at 17:33
4

I've just been hitting this problem when checking out a repo to a home directory mounted via NFS on Ubuntu 14.04 (Trusty) using the backported Xenial version 4.x linux Kernel. Git clone to a local directory was fine. Even more odd: A second Ubuntu 14.04 server did not exhibit the same problem on the same mounted directory.

After a lot of poking around I was able to see using strace that git called the open() system call to create each file with options O_CREAT,O_WRONLY and O_EXCL and a mode of 0666, but then the next syscal was an fstat() against the file and told me it was mode 0700 . In my case the problem only affected certain files in the repo. Despite 'git ls-index' showing mode 0644 for most files, some of them were being created correctly and others not; although it was always the same files which had wrong permissions on clone.

I noticed that there was a difference in Kernel version between the two systems and then discovered the following bug: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1654288

Upgrading the kernel to 4.4.0-98 (from 4.4.0-59) fixed this for me. I checked some hosts still using the version 3.x Linux Kernel and these did not have problem.

1

Using hooks to change file mode after checkout is fixing the problem after it already ocurred. You already have bad file mode in the filesystem when executing the hook. If a request arrives just between the checkout and hook execution, the server will respond with the 500 error. But you may be interested in this solution anyway.

You need a post-checkout hook running chmod g-w on all the files necessary. The hook is .git/hooks/post-checkout, should be executable and gets the current HEAD as second parameter ($2 in shell). The hook could look like this:

#!/bin/bash
git ls-files -z --with-tree="$2" | xargs -0 chmod g-w --

As the hook does not get list of files checked out, this may be the best implementation possible. It changes the mode of all the files in current HEAD.

Palec
  • 10,298
  • 7
  • 52
  • 116