Velvet Star Monitor

Standout celebrity highlights with iconic style.

news

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

Writer Andrew Henderson

I have a git checkout. All the file permissions are different than what git thinks they should be therefore they all show up as modified.

Without touching the content of the files (just want to modify the permissions) how do I set all the files permissions to what git thinks they should be?

1

12 Answers

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.

30

Try git config core.fileMode false

From the git config man page:

core.fileMode

If false, the executable bit differences between the index and the working copy are ignored; useful on broken filesystems like FAT. See git-update-index(1).

The default is true, except git-clone(1) or git-init(1) will probe and set core.fileMode false if appropriate when the repository is created.

4

Git doesn't store file permissions other than executable scripts. Consider using something like git-cache-meta to save file ownership and permissions.

Git can only store two types of modes: 755 (executable) and 644 (not executable). If your file was 444 git would store it has 644.

4
git diff -p \
| grep -E '^(diff|old mode|new mode)' \
| sed -e 's/^old/NEW/;s/^new/old/;s/^NEW/new/' \
| git apply

will work in most cases but if you have external diff tools like meld installed you have to add --no-ext-diff

git diff --no-ext-diff -p \ | grep -E '^(diff|old mode|new mode)' \ | sed -e 's/^old/NEW/;s/^new/old/;s/^NEW/new/' \ | git apply

was needed in my situation

0

i know this is old, but i came from google and i didn't find an answer

i have a simple solution if you have no change you want to keep :

git config core.fileMode true
git reset --hard HEAD

Thanks @muhqu for his great answer. In my case not all changes files had permissions changed which prevented the command to work.

$ git diff -p -R --no-ext-diff --no-color | grep -E "^(diff|(old|new) mode)" --color=never
diff --git b/file1 a/file1
diff --git b/file2 a/file2
old mode 100755
new mode 100644
$ git diff -p -R --no-ext-diff --no-color | grep -E "^(diff|(old|new) mode)" --color=never | git apply
warning: file1 has type 100644, expected 100755

The patch would then stop and files would be left untouched.

In case some people have similar problem I solved this by tweaking the command to grep only files with permission changed:

grep -E "^old mode (100644|100755)" -B1 -A1

or for the git alias

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

I run into a similar problem, someone added the executable flag to all the files on the server, however I also had local modified files besides the ones with the broken permissions. However, since the only permission git tracks is the executable flag, this pipeline fixed the problem for me:

git status | grep 'modified:' | awk '{print $3}' | xargs chmod a-x

Basically the command runs git status, filters the files reported as modifier, extracts their path via awk, and removes the executable flag.

1

git diff -p used in muhqu's answer may not show all discrepancies.

  • saw this in Cygwin for files I didn't own
  • mode changes are ignored completely if core.filemode is false (which is the default for MSysGit)

This code reads the metadata directly instead:

(set -o errexit pipefail nounset;
git ls-tree HEAD -z | while read -r -d $'\0' mask type blob path
do if [ "$type" != "blob" ]; then continue; fi; case "$mask" in #do not touch other bits 100644) chmod a-x "$path";; 100755) chmod a+x "$path";; *) echo "invalid: $mask $type $blob\t$path" >&2; false;; esac
done)

A non-production-grade one-liner (replaces masks entirely):

git ls-tree HEAD | perl -ne '/^10(0\d{3}) blob \S+\t(.+)$/ && { system "chmod",$1,$2 || die }'

(Credit for "$'\0'" goes to )

You could also try a pre/post checkout hook might do the trick.

See: Customizing Git - Git Hooks

I use git from cygwin on Windows, the git apply solution doesn't work for me. Here is my solution, run chmod on every file to reset its permissions.

#!/bin/bash
IFS=$'\n'
for c in `git diff -p |sed -n '/diff --git/{N;s/diff --git//g;s/\n/ /g;s# a/.* b/##g;s/old mode //g;s/\(.*\) 100\(.*\)/chmod \2 \1/g;p}'`
do eval $c
done
unset IFS

The easiest thing to do is to just change the permissions back. As @kroger noted git only tracks executable bits. So you probably just need to run chmod -x filename to fix it (or +x if that's what's needed.

2

The etckeeper tool can handle permissions and with:

etckeeper init -d /mydir

You can use it for other dirs than /etc.

Install by using your package manager or get sources from above link.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy