问题
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:
Git keeps track of filepermission and exposes permission changes when creating patches using git diff -p
. So all we need is:
- create a reverse patch
- include only the permission changes
- apply the patch to our working copy
As a one-liner:
git diff -p -R --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-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.
回答2:
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.
回答3:
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
回答5:
You could also try a pre/post checkout hook might do the trick.
See: Customizing Git - Git Hooks
回答6:
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.
回答7:
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
isfalse
(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)
回答8:
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.
来源:https://stackoverflow.com/questions/2517339/how-to-restore-the-permissions-of-files-and-directories-within-git-if-they-have