Сохранение прав доступа к файлам с помощью Git

109

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

Очевидно, излом в моем плане состоит в том, что Git не будет уважать права доступа к файлам (я не пробовал, только сейчас читаю об этом). Я думаю, это имеет смысл, поскольку разные блоки могут иметь разные настройки пользователей / групп. Но если бы я хотел принудительно распространять разрешения, зная, что мои серверы настроены одинаково, есть ли у меня какие-либо варианты? Или есть более простой способ приблизиться к тому, что я пытаюсь сделать?

Ярина
источник
1
Да, думаю, да, хотя решение, на которое они указывают, я, честно говоря, не знаю, что делать. Надеялся на более прямой подход.
Yarin
Как насчет ситуации, когда исходный код поступает из среды Dev (например, Windows - XAMPP и т. Д.), В которой нет информации о владельце файла? Файлы в конце процесса git должны соответствовать владению и разрешениям для целевого местоположения. Может ли git-cache-meta с этим справиться? Согласитесь с Ярином ... конечно, это довольно распространенный вариант использования, у которого должно быть довольно простое решение?
user3600150 01

Ответы:

43

git-cache-metaУпоминаются в SO вопроса « мерзавец - как восстановить права доступа к файлам мерзавец посчитает файл должен быть » (и мерзавец FAQ ) является более 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

Так что вы:

  • объедините свое репо и сохраните связанные разрешения для файлов.
  • скопируйте эти два файла на удаленный сервер
  • восстановите репо там и примените разрешение
VonC
источник
2
VonC - Спасибо, попробую - а нужна ли комплектация? Не могу ли я сохранить свой рабочий процесс (dev -> github -> production) и просто проверить / проверить метафайл?
Yarin
@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 заботится об исполняемом бите и только об этом?
Yarin
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.

.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 / hooks / после оформления заказа:

#!/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 04
@Ricalsin Добро пожаловать! Рад, что помог :)
Омид Ариян
1
$SELF_DIR/../../не обязательно является корнем репозитория ... но git rev-parse --show-toplevelесть. (Не уверен, почему бы вам просто не использовать pwdтекущий каталог, но это все равно спорный.)
PJSCopeland
Как бы то ни было, приведенное выше разделит имена файлов с пробелами. В соответствии с этим ответом вы можете установить IFS=$'\n'перед forциклом, чтобы остановить это (а unset IFSзатем, чтобы быть в безопасности).
PAOopeland
Это не позволяет вам переносить разрешения в другую систему с другой ОС, где у вас другое имя пользователя. Я спросил себя: «Что мне действительно нужно?» и сократите весь раствор до chmod 0600 .pgpassдюйма post-checkout. Да, мне придется обновлять его вручную всякий раз, когда у меня есть файл, требующий определенных разрешений, но это перерывы.
PAOopeland
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. В нем одна строка на файл, вот верхняя часть моего примера:

$ 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

В комментариях к этому подходу один из плакатов жалуется, что он работает только с тем же именем пользователя, и это технически верно, но это очень легко исправить. Обратите внимание на то, что скрипт после оформления заказа состоит из двух частей:

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

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

В первом ответе в этом сообщении, наиболее широко принятом, предлагается использовать git-cache-meta, скрипт, который выполняет ту же работу, что и скрипты pre / post hook здесь (анализ вывода из git ls-files) . Эти сценарии мне легче понять, код git-cache-meta более сложен. Можно сохранить git-cache-meta в пути и написать сценарии до фиксации и после проверки, которые будут его использовать.

Пробелы в именах файлов - проблема обоих скриптов Омида. В сценарии после оформления заказа вы будете знать, что у вас есть пробелы в именах файлов, если вы увидите такие ошибки

$ 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 в прежнее состояние! Вы можете понять, почему сеанс оболочки может закончиться неудачно, если вы оставите $ в качестве единственного разделителя.

pauljohn32
источник
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 / hooks / после слияния

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

Обратите внимание, мне пришлось внести несколько изменений в параметры статистики.

Наслаждаться,

Альбаро Перейра
источник
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

Это также сохранит права доступа к каталогу.

Питер Бербек
источник