Retaining file permissions with Git Retaining file permissions with Git linux linux

Retaining file permissions with Git


Git is Version Control System, created for software development, so from the whole set of modes and permissions it stores only executable bit (for ordinary files) and symlink bit. If you want to store full permissions, you need third party tool, like git-cache-meta (mentioned by VonC), or Metastore (used by etckeeper). Or you can use IsiSetup, which IIRC uses git as backend.

See Interfaces, frontends, and tools page on Git Wiki.


The git-cache-meta mentioned in SO question "git - how to recover the file permissions git thinks the file should be?" (and the git FAQ) is the more staightforward approach.

The idea is to store in a .git_cache_meta file the permissions of the files and directories.
It is a separate file not versioned directly in the Git repo.

That is why the usage for it is:

$ git bundle create mybundle.bdl master; git-cache-meta --store$ scp mybundle.bdl .git_cache_meta machine2: #then on machine2:$ git init; git pull mybundle.bdl master; git-cache-meta --apply

So you:

  • bundle your repo and save the associated file permissions.
  • copy those two files on the remote server
  • restore the repo there, and apply the permission


This is quite late but might help some others. I do what you want to do by adding two git hooks to my repository.

.git/hooks/pre-commit:

#!/bin/bash## A hook script called by "git commit" with no arguments. The hook should# exit with non-zero status after issuing an appropriate message if it wants# to stop the commit.SELF_DIR=`git rev-parse --show-toplevel`DATABASE=$SELF_DIR/.permissions# Clear the permissions database file> $DATABASEecho -n "Backing-up permissions..."IFS_OLD=$IFS; IFS=$'\n'for FILE in `git ls-files --full-name`do   # Save the permissions of all the files in the index   echo $FILE";"`stat -c "%a;%U;%G" $FILE` >> $DATABASEdonefor DIRECTORY in `git ls-files --full-name | xargs -n 1 dirname | uniq`do   # Save the permissions of all the directories in the index   echo $DIRECTORY";"`stat -c "%a;%U;%G" $DIRECTORY` >> $DATABASEdoneIFS=$IFS_OLD# Add the permissions database file to the indexgit add $DATABASE -fecho "OK"

.git/hooks/post-checkout:

#!/bin/bashSELF_DIR=`git rev-parse --show-toplevel`DATABASE=$SELF_DIR/.permissionsecho -n "Restoring permissions..."IFS_OLD=$IFS; IFS=$'\n'while read -r LINE || [[ -n "$LINE" ]];do   ITEM=`echo $LINE | cut -d ";" -f 1`   PERMISSIONS=`echo $LINE | cut -d ";" -f 2`   USER=`echo $LINE | cut -d ";" -f 3`   GROUP=`echo $LINE | cut -d ";" -f 4`   # Set the file/directory permissions   chmod $PERMISSIONS $ITEM   # Set the file/directory owner and groups   chown $USER:$GROUP $ITEMdone < $DATABASEIFS=$IFS_OLDecho "OK"exit 0

The first hook is called when you "commit" and will read the ownership and permissions for all the files in the repository and store them in a file in the root of the repository called .permissions and then add the .permissions file to the commit.

The second hook is called when you "checkout" and will go through the list of files in the .permissions file and restore the ownership and permissions of those files.

  • You might need to do the commit and checkout using sudo.
  • Make sure the pre-commit and post-checkout scripts have execution permission.