Gitでファイルのアクセス許可を保持する


109

Webサーバーのバージョン管理で説明されているように 、自分のgitリポジトリを作成して、Webサーバーのバージョン管理をしたい/var/www directory。私の希望は、開発サーバーからgithubにWebコンテンツをプッシュし、本番サーバーにプルして、残りの時間をプールで過ごせることです。

どうやら私の計画のねじれはGitがファイルのアクセス許可を尊重しないことです(私はそれを試していません、今それについて読むだけです)。私はこれは、異なるボックスが異なるユーザー/グループ設定を持つ傾向があるという点で理にかなっていると思います。しかし、自分のサーバーが同じように構成されていることを知っていて、アクセス許可を強制的に伝達したい場合、オプションはありますか?または、私がやろうとしていることにアプローチする簡単な方法はありますか?


1
可能性のあるgitの
kennytm 2010

1
ええ、そうだと思いますが、私が率直に言っている解決策は、どうすればよいかわかりません。より簡単なアプローチを望んでいました。
Yarin

ファイル所有権情報のない開発環境(Windows-XAMPPなど)からソースコードが送信されている状況についてはどうですか?gitプロセスの最後のファイルは、ターゲットの場所の所有権と権限を一致させる必要があります。git-cache-metaはこれに対処できますか?Yarinに同意する...確かにこれはかなり主流のユースケースであり、かなり簡単な解決策があるはずですか?
user3600150 2016

回答:


43

git-cache-meta「SOの質問で述べた?gitの-どのようにファイルがあるべきと考えてgitのファイルのアクセス権を回復する」(とgitのよくある質問は)もっとstaightforwardアプローチです。

アイデアは.git_cache_meta、ファイルとディレクトリの権限をファイルに保存することです。
これは、Gitリポジトリで直接バージョン管理されない個別のファイルです。

そのため、その使用法は次のとおりです。

$ 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

だからあなた:

  • リポジトリバンドルし、関連するファイル権限を保存します。
  • これら2つのファイルをリモートサーバーにコピーする
  • そこでリポジトリを復元し、権限を適用します

2
VonC-ありがとうございます。試してみますが、バンドルは必要ですか?ワークフロー(dev-> github-> production)を保持して、メタファイルをチェックイン/チェックアウトすることはできませんか?
Yarin

@ヤーリン:いいえ、バンドルは必須ではありません。他の転送プロトコルが利用できない場合でも、これはリポジトリを転送するための適切な方法です。
VonC、2010

3
ここでバンドルを使用することは、私にとって大きな注意散漫でした。実際、私は答えを完全に先送りにしました。(サーバーからリポをプルするのに問題はありません。)以下のプリ/ポストコミットフックを使用した@ omid-ariyanの回答は、はるかに理解しやすくなりました。後で、これらのフックスクリプトがgit-cache-metaとまったく同じ動作をしていることに気付きました。私が何を言っているのか見てみましょうgist.github.com/andris9/1978266。それらは、からの戻りを解析および格納していgit ls-filesます。
pauljohn32

git-cache-metaへのリンクは死んでいます-これについて知っている誰かがそれを見つけて投稿を編集できますか?
rosuav 2016年

@rosuav確かに:私は回答を編集してリンクを復元しました。このリンク切れについてお知らせいただきありがとうございます。
VonC 2016年

63

Gitは、ソフトウェア開発用に作成されたバージョンコントロールシステムであるため、モードと権限のセット全体から、実行可能ビット(通常のファイル用)とシンボリックリンクビットのみを保存します。完全なアクセス許可を保存する場合は、git-cache-metaVonCで言及されている)などのサードパーティツール、または(etckeeperで使用される)メタストアが必要です。または、IIRCがgitをバックエンドとして使用するIsiSetupを使用することもできます。

参照インターフェース、フロントエンド、およびツール GitのWikiのページを。


2
Jakubに感謝します。なぜGitが実行可能ビットに関心があるのか​​、そしてそれだけを説明してくれますか?
Yarin、2010

5
@ヤリン:実行可能ビットのみ?あるシステムから別のシステムにすべてのファイルセットのクローンを作成する場合、「読み取り専用」または「読み取り/書き込み」の概念は正確には関係ありません(質問で述べたように、異なるユーザー/グループ)。ただし、「実行可能」の概念はユーザーやグループに依存せず、システムから(リモート)システムに再利用できます。
VonC、2010

1
Jakub、その場合、権限を変更すべきではありません。つまり、パーマをそのままにするか管理する必要がありますが、管理するつもりがない場合は、混乱しないでください。
CommaToast 14

3
さらに、パッケージにも同様の目的のスクリプトが見つかりまし/usr/share/git-core/contrib/hooks/setgitperms.perlgit-contrib。(「このスクリプトは、gitワーキングツリー内の完全な権限と所有権データを保存/復元するために使用できます。」)
imz-Ivan Zakharyaschev

これはまだ正確ですか、githubはどういうわけかgitの上で何かをしますか?ファイルを実行可能ファイルに変更してコミットしました。コミットの変更ログには、ファイルの変更された行が0行と表示されていますが、ファイル名の横に100644→100755と表示されています。これは、完全な権限がファイルに保存されているように見えます。
ランチャー

23

これはかなり遅いですが、他の人を助けるかもしれません。リポジトリに2つのgitフックを追加して、やりたいことを行います。

.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
> $DATABASE

echo -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` >> $DATABASE
done

for 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` >> $DATABASE
done
IFS=$IFS_OLD

# Add the permissions database file to the index
git add $DATABASE -f

echo "OK"

.git / hooks / post-checkout:

#!/bin/bash

SELF_DIR=`git rev-parse --show-toplevel`
DATABASE=$SELF_DIR/.permissions

echo -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 $ITEM

done < $DATABASE
IFS=$IFS_OLD

echo "OK"

exit 0

最初のフックは「コミット」時に呼び出され、リポジトリ内のすべてのファイルの所有権と権限を読み取り、それらを.permissionsと呼ばれるリポジトリのルートにあるファイルに保存して、.permissionsファイルをコミットに追加します。

2番目のフックは、「チェックアウト」時に呼び出され、.permissionsファイル内のファイルのリストを調べて、それらのファイルの所有権と権限を復元します。

  • sudoを使用してコミットとチェックアウトを行う必要がある場合があります。
  • コミット前およびチェックアウト後のスクリプトに実行権限があることを確認してください。

Omid ...ありがとうございます!私はあなたのコードが私にとって完璧な解決策であることを発見しました。
Ricalsin

@Ricalsinどういたしまして!:)
Omid Ariyan

1
$SELF_DIR/../../は必ずしもリポジトリのルートとは限りませんが、そうgit rev-parse --show-toplevelです。(なぜpwd現在のディレクトリに使用しないのかわからないが、とにかくそれは意味がない。)
PJSCopeland

現状では、上記はスペースを含むファイル名を分割します。この回答に従ってIFS=$'\n'forループの前に設定して、それを停止することができます(unset IFS後で安全にするため)。
PJSCopeland

これでは、ユーザー名が異なる別のOSの別のシステムに権限を簡単に運ぶことはできません。「本当に何が必要なの?」と自問しましたとにソリューション全体の削減chmod 0600 .pgpassではpost-checkout。はい、特定のアクセス許可が必要なファイルがある場合は、手動で更新する必要がありますが、それが問題です。
PJSCopeland

2

あなたが今これに参加している場合のために、私は今日それを経験しました、そしてこれがどこに立っているかを要約できます。これをまだ試していない場合は、ここでいくつかの詳細が役立つ場合があります。

@Omid Ariyanのアプローチが最良の方法だと思います。コミット前およびチェックアウト後のスクリプトを追加します。Omidと同じように正確に名前を付けることを忘れないでください。また、実行可能にすることを忘れないでください。これらのいずれかを忘れた場合、それらは効果がなく、何も起こらないのではないかと疑問に思って何度も "git commit"を実行します:)また、Webブラウザーからカットアンドペーストする場合は、引用符とチェックマークに注意してください変更されました。

(git commitを実行して)pre-commitスクリプトを1回実行すると、ファイル.permissionsが作成されます。リポジトリに追加できますが、プリコミットスクリプトの最後に何度も追加する必要はないと思います。でも、害はないと思います(希望)。

Omidのスクリプトでは、ディレクトリ名とファイル名にスペースが含まれていることについて、いくつかの小さな問題があります。ここではスペースが問題で、IFSの修正で問題が発生しました。参考までに、このpre-commitスクリプトは私にとっては正しく機能しました。

#!/bin/bash  

SELF_DIR=`git rev-parse --show-toplevel`
DATABASE=$SELF_DIR/.permissions

# Clear the permissions database file
> $DATABASE

echo -n "Backing-up file permissions..."

IFSold=$IFS
IFS=$'\n'
for FILE  in `git ls-files`
do
   # Save the permissions of all the files in the index
   echo $FILE";"`stat -c "%a;%U;%G" $FILE` >> $DATABASE
done
IFS=${IFSold}
# Add the permissions database file to the index
git add $DATABASE

echo "OK"

さて、これから何を得るのでしょうか?

.permissionsファイルはgitリポジトリの最上位にあります。ファイルごとに1行あります。これが私の例の先頭です。

$ cat .permissions
.gitignore;660;pauljohn;pauljohn
05.WhatToReport/05.WhatToReport.doc;664;pauljohn;pauljohn
05.WhatToReport/05.WhatToReport.pdf;664;pauljohn;pauljohn

ご覧のとおり、

filepath;perms;owner;group

このアプローチに関するコメントで、投稿者の1人が、同じユーザー名でのみ機能すること、そしてそれは技術的には真実であるが、それを修正することは非常に簡単であると不平を言っています。チェックアウト後のスクリプトには2つのアクションがあります。

# Set the file permissions
chmod $PERMISSIONS $FILE
# Set the file owner and groups
chown $USER:$GROUP $FILE

だから私は最初のものだけを残している、それが私が必要なすべてです。Webサーバー上の私のユーザー名は確かに異なりますが、より重要なのは、rootでない限り、chownを実行できないことです。ただし、「chgrp」は実行できます。それをどのように使用するかは十分に明白です。

この投稿の最初の回答は、最も広く受け入れられているものですが、提案は、git-cache-metaを使用することです。 git ls-files)。 。これらのスクリプトは私にとって理解しやすく、git-cache-metaコードはかなり複雑です。パスにgit-cache-metaを保持して、それを使用するコミット前およびチェックアウト後のスクリプトを作成することができます。

ファイル名にスペースが含まれていると、Omidの両方のスクリプトで問題が発生します。チェックアウト後のスクリプトでは、このようなエラーが表示された場合、ファイル名にスペースが含まれていることがわかります

$ git checkout -- upload.sh
Restoring file permissions...chmod: cannot access  '04.StartingValuesInLISREL/Open': No such file or directory
chmod: cannot access 'Notebook.onetoc2': No such file or directory
chown: cannot access '04.StartingValuesInLISREL/Open': No such file or directory
chown: cannot access 'Notebook.onetoc2': No such file or directory

その解決策を確認しています。これはうまくいくようですが、私は1つのケースでのみテストしました

#!/bin/bash

SELF_DIR=`git rev-parse --show-toplevel`
DATABASE=$SELF_DIR/.permissions

echo -n "Restoring file permissions..."
IFSold=${IFS}
IFS=$
while read -r LINE || [[ -n "$LINE" ]];
do
   FILE=`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 permissions
   chmod $PERMISSIONS $FILE
   # Set the file owner and groups
   chown $USER:$GROUP $FILE
done < $DATABASE
IFS=${IFSold}
echo "OK"

exit 0

許可情報は一度に1行なので、IFSを$に設定し、改行だけが新しいものとして表示されるようにしました。

IFS環境変数を元の状態に戻すことは非常に重要であると読みました。$を唯一のセパレーターのままにしておくと、シェルセッションがうまくいかない理由がわかります。


2

.permissionsファイルの形式を実行可能なchmodステートメントに変更し、-printfパラメーターをに使用することで、他の回答を改善できますfind。これはより単純な.git/hooks/pre-commitファイルです:

#!/usr/bin/env bash

echo -n "Backing-up file permissions... "

cd "$(git rev-parse --show-toplevel)"

find . -printf 'chmod %m "%p"\n' > .permissions

git add .permissions

echo done.

...そしてここに簡略化した.git/hooks/post-checkoutファイルがあります:

#!/usr/bin/env bash

echo -n "Restoring file permissions... "

cd "$(git rev-parse --show-toplevel)"

. .permissions

echo "done."

他のツールがこれらのスクリプトをすでに構成している可能性があるため、それらを一緒にマージする必要がある場合があることに注意してください。たとえば、次のpost-checkoutスクリプトにはgit-lfsコマンドも含まれています。

#!/usr/bin/env bash

echo -n "Restoring file permissions... "

cd "$(git rev-parse --show-toplevel)"

. .permissions

echo "done."

command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on you
r path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-checkout.\n"; exit 2; }
git lfs post-checkout "$@"

1

コミット前/チェックアウト後のオプションは、「mtree」(FreeBSD)または「fmtree」(Ubuntu)ユーティリティを使用して、「ファイル階層を仕様と比較したり、ファイル階層の仕様を作成したり、仕様。"

デフォルトのセットは、フラグ、gid、リンク、モード、nlink、サイズ、時間、タイプ、およびuidです。これは、-kスイッチを使用して特定の目的に合わせることができます。


1

私はFreeBSD 11.1で実行しています。freebsdjail仮想化の概念により、オペレーティングシステムが最適化されます。私が使用しているGitの現在のバージョンは2.15.1ですが、すべてをシェルスクリプトで実行することも好みます。このことを念頭に置いて、上記の提案を次のように変更しました。

git push:.git / hooks / pre-commit

#! /bin/sh -
#
# 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
> $DATABASE;

printf "Backing-up file permissions...\n";

OLDIFS=$IFS;
IFS=$'\n';
for FILE in $(git ls-files);
do
   # Save the permissions of all the files in the index
    printf "%s;%s\n" $FILE $(stat -f "%Lp;%u;%g" $FILE) >> $DATABASE;
done
IFS=$OLDIFS;

# Add the permissions database file to the index
git add $DATABASE;

printf "OK\n";

git pull:.git / hooks / post-merge

#! /bin/sh -

SELF_DIR=$(git rev-parse --show-toplevel);
DATABASE=$SELF_DIR/.permissions;

printf "Restoring file permissions...\n";

OLDIFS=$IFS;
IFS=$'\n';
while read -r LINE || [ -n "$LINE" ];
do
   FILE=$(printf "%s" $LINE | cut -d ";" -f 1);
   PERMISSIONS=$(printf "%s" $LINE | cut -d ";" -f 2);
   USER=$(printf "%s" $LINE | cut -d ";" -f 3);
   GROUP=$(printf "%s" $LINE | cut -d ";" -f 4);

   # Set the file permissions
   chmod $PERMISSIONS $FILE;

   # Set the file owner and groups
   chown $USER:$GROUP $FILE;

done < $DATABASE
IFS=$OLDIFS

pritnf "OK\n";

exit 0;

何らかの理由でスクリプトを再作成する必要がある場合、.permissionsファイルの出力は次の形式になります。

.gitignore;644;0;0

root:wheelに644の権限が付与された.gitignoreファイルの場合

statオプションにいくつかの変更を加える必要があったことに注意してください。

楽しい、


1

@Omid Ariyanの答えの1つに、ディレクトリに対する権限があります。これをforループのスクリプトの後に追加donepre-commitます。

for DIR in $(find ./ -mindepth 1 -type d -not -path "./.git" -not -path "./.git/*" | sed 's@^\./@@')
do
    # Save the permissions of all the files in the index
    echo $DIR";"`stat -c "%a;%U;%G" $DIR` >> $DATABASE
done

これにより、ディレクトリのアクセス許可も保存されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.