开发者

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

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开发者_C百科 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?


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.


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.


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.


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


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.


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 http://transnum.blogspot.ru/2008/11/bashs-read-built-in-supports-0-as.html)


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜