Збереження дозволів на файли за допомогою Git


109

Я хочу керувати версіями свого веб-сервера, як описано в « Версії управління» для мого веб-сервера , створивши git repo з мого /var/www directory. Я сподівався, що мені вдасться перенести веб-контент з нашого сервера розробників на github, перетягнути його на наш виробничий сервер та провести решту дня у басейні.

Мабуть, в моєму плані перелом є те, що Git не буде дотримуватися дозволів на файли (я не пробував цього, лише читаю про це зараз.) Я думаю, це має сенс у тому, що різні поля можуть мати різні налаштування користувачів та груп. Але якщо я хотів змусити дозволу поширюватися, знаючи, що мої сервери налаштовані однаково, чи є у мене варіанти? Або є простіший спосіб підійти до того, що я намагаюся зробити?



1
Гадаю, так, хоч у вирішенні, на яке вони вказують, я відверто не впевнений, що з цим робити. Сподівався на більш прямолінійний підхід.
Ярін

Як щодо ситуації, коли вихідний код надходить із середовища Dev (наприклад, Windows - XAMPP тощо), у якому немає інформації про право власності на файл? Файли в кінці процесу git повинні відповідати право власності та дозволи для цільового місця розташування. Чи може з цим впоратися git-cache-meta? Погодьтеся з Ярін ... напевно, це досить гарний випадок використання, який повинен мати досить просте рішення?
user3600150

Відповіді:


43

git-cache-metaЗгадуються в SO питання « мерзотник - як відновити права доступу до файлів мерзотник вважатиме файл повинен бути » (і мерзотник FAQ ) є більш staightforward підходу.

Ідея полягає у збереженні у .git_cache_metaфайлі дозволів на файли та каталоги.
Це окремий файл, не виконаний безпосередньо в Git repo.

Ось чому використання для нього:

$ 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
VonC- Спасибі за це, я спробую це випробувати, але чи необхідна група? Не можу я зберегти свій робочий процес (dev -> github -> production) і просто перевірити / оформити метафайл?
Ярін

@Yarin: ні, пакет не є обов'язковим. Це акуратний спосіб передачі репо, коли немає іншого протоколу передачі.
VonC

3
Використання в'язки тут було для мене головним відволіканням. Це фактично мене відклало від відповіді. (У мене немає труднощів із витягненням репо з сервера.) @ Відповідь omid-ariyan нижче за допомогою гачків до / після фіксації була набагато зрозумілішою. Пізніше я зрозумів, що ці сценарії з гаками виконують точно таку ж роботу, як і git-cache-meta. Подивіться, що я маю на увазі: gist.github.com/andris9/1978266 . Вони аналізують і зберігають повернення з git ls-files.
pauljohn32

Посилання на git-cache-meta мертве - може хтось, хто знає про це, знайти його та відредагувати публікацію?
rosuav

@rosuav Звичайно: я відредагував відповідь і відновив посилання. Дякую, що повідомили мені про це мертве посилання.
VonC

63

Git - це система управління версіями, створена для розробки програмного забезпечення, тому з усього набору режимів і дозволів вона зберігає лише виконуваний біт (для звичайних файлів) та біт символьного посилання. Якщо ви хочете зберегти повні права доступу, необхідно сторонній інструмент, як git-cache-meta( згадуваний VonC ) або Metastore (використовуваний etckeeper ). Або ви можете використовувати IsiSetup , який IIRC використовує git як бекенд.

Див. Сторінку Інтерфейси, фронти та інструменти на Git Wiki.


2
Спасибі Якуб, ти можеш пояснити моєму, чому Git піклується про виконавчий біт і тільки це?
Ярін

5
@Yarin: лише виконавчий біт? Коли ви клонуєте весь набір файлів з однієї системи в іншу, поняття "лише для читання" або "читання-запис" не є абсолютно актуальним (як ви сказали у своєму запитанні: різні користувачі / групи). Але поняття "виконуваний файл" не залежить від користувачів та груп, і його можна повторно використовувати від системи до (віддаленої) системи.
VonC

1
Якуб, у такому випадку він не повинен змінювати дозволи. Я маю на увазі, він повинен залишати пермських у спокої або керувати ними, але не возитися з ними, якщо він не збирається ними керувати.
CommaToast

3
Крім того, я знайшов /usr/share/git-core/contrib/hooks/setgitperms.perlу своєму git-contribпакеті - сценарій з подібною метою. ("Цей скрипт можна використовувати для збереження / відновлення повних дозволів та даних про право власності в робочому дереві git.")
imz - Іван Захарящев

Це все ще точно або github якось щось робить на додаток до git? Я просто змінив файл на виконуваний файл і вчинив його, і журнал змін для фіксації відображається як 0 рядків, змінених для файлу, але поряд з ім'ям файлу є 100644 → 100755. Це дійсно схоже на те, що повні дозволи зберігаються разом із файлом.
Cruncher

23

Це досить пізно, але може допомогти іншим. Я роблю те, що ви хочете зробити, додаючи до мого сховища два гакі.

.git / гачки / попереднє здійснення:

#!/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 / гачки / після оформлення замовлення:

#!/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 до коміту.

Другий гак викликається під час "оформлення замовлення" і пройде через список файлів у файлі .permissions та відновить право власності та дозволи цих файлів.

  • Можливо, вам знадобиться зробити переклади та оформлення замовлення, використовуючи sudo.
  • Переконайтесь, що сценарії до завершення та після оформлення замовлення мають дозвіл на виконання.

Омід ... дякую! Я знайшов ваш код для мене ідеальним рішенням.
Ricalsin

@Ricalsin Ви дуже раді! Я радий, що допомогли :)
Омід Аріан

1
$SELF_DIR/../../не обов'язково є коренем сховища ... але git rev-parse --show-toplevelє. (Не впевнений, чому б ви не просто використовували pwdдля поточного каталогу, але це все одно спірно.)
PJSCopeland

Як відомо, вищенаведені файли розділять імена файлів з пробілами. Відповідно до цієї відповіді , ви можете встановити IFS=$'\n'перед forциклом, щоб зупинити це (і unset IFSпісля цього бути безпечним).
PJSCopeland

Це не дозволяє легко переносити дозволи до іншої системи, з іншою ОС, де у вас є інше ім’я користувача. Я запитав себе "що мені насправді потрібно?" і скоротити всі вниз рішення , щоб chmod 0600 .pgpassв post-checkout. Так, мені доведеться оновлювати його вручну кожного разу, коли у мене є файл, який потребує певних дозволів, але це перерви.
PJSCopeland

2

Якщо ви зараз вступаєте в цю справу, я щойно пережив це і можу підсумувати, де це стоїть. Якщо ви ще цього не пробували, деякі деталі тут можуть допомогти.

Я думаю, що підхід @Omid Ariyan - найкращий спосіб. Додайте сценарії до завершення та після оформлення замовлення. НЕ забудьте назвати їх саме так, як це робить Омід, і НЕ забудьте зробити їх виконаними. Якщо ви забудете будь-яке з них, вони не мають ефекту, і ви знову і знову запускаєте "git commit", цікаво, чому нічого не відбувається :) Крім того, якщо вирізати та вставити з веб-браузера, будьте уважні, щоб лапки та клітинки не були перероблений.

Якщо запустити скрипт перед фіксацією один раз (запустивши git commit), тоді файли .permissions будуть створені. Ви можете додати його до сховища, і я вважаю, що не потрібно додавати його знову і знову в кінці сценарію попереднього введення. Але це не шкодить, я думаю (сподіваюся).

Існує кілька невеликих проблем щодо імені каталогу та наявності пробілів у іменах файлів у сценаріях Omid. Пробіли тут були проблемою, і у мене виникли проблеми з виправленням IFS. Для запису цей сценарій перед фіксацією працював правильно для мене:

#!/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 repo. У ньому є один рядок у файлі, ось верхня частина мого прикладу:

$ 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

У коментарях щодо такого підходу один із плакатів скаржиться, що він працює лише з тим самим іменем користувача, і це технічно правда, але це дуже легко виправити. Зверніть увагу, що сценарій після оформлення замовлення містить 2 дії,

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

Тож я зберігаю лише перший, це все, що мені потрібно. Моє ім’я користувача на веб-сервері дійсно різне, але що ще важливіше, ви не можете запускати chown, якщо ви не root. Однак можна запустити "chgrp". Досить зрозуміло, як це використовувати.

У першій відповіді в цій публікації, найбільш широко прийнятій, пропозиція полягає в тому, щоб використовувати git-cache-meta, скрипт, який виконує ту саму роботу, що і тут виконуються скрипти перед гаком до / після (аналіз результатів git ls-files) . Ці сценарії мені легше зрозуміти, мета-код git-cache-мета є більш досконалим. Можна зберігати 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

Я перевіряю рішення для цього. Ось щось, здається, працює, але я протестував лише в одному випадку

#!/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

Оскільки інформація про дозволи є одним рядком, я встановлюю 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, концепція віртуалізації в'язниці freebsd робить операційну систему оптимальною. Поточна версія Git, яку я використовую, є 2.15.1, я також вважаю за краще виконувати все на скриптах оболонки. Зважаючи на це, я змінив наведені вище пропозиції наступним чином:

git push: .git / гачки / попередньо здійснити

#! /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 / гачки / після злиття

#! /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

Для файлу .gitignore з 644 дозволу, що задаються root: wheel

Зауважте, що мені довелося внести кілька змін у параметри stat.

Насолоджуйтесь,


1

Одним із доповнень відповіді @Omid Ariyan є дозволи на каталоги. Додайте це після forциклу doneв його pre-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

Це також збереже дозволи для каталогу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.