Сохранение разрешений файлов с помощью Git

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

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

Ответ 1

git-cache-meta упомянутый в SO вопросе " git - как восстановить права доступа к файлу, git считает, что файл должен быть? " (И git FAQ) - более простой подход.

Идея состоит в том, чтобы сохранить в файле .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

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

Смотрите Интерфейсы, интерфейсы и инструменты на странице git Wiki.

Ответ 3

Это довольно поздно, но может помочь другим. Я делаю то, что вы хотите, добавив два 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/Крючки/пост-фотографии:

#!/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.
  • Убедитесь, что у скриптов pre-commit и post-checkout есть разрешение на выполнение.

Ответ 4

В случае, если вы сейчас входите в это право, я только что прошел через него и могу обобщить, где это стоит. Если вы еще не пытались это сделать, некоторые детали здесь могут помочь.

Я думаю, что подход @Omid Ariyan - лучший способ. Добавьте сценарии pre-commit и post-checkout. НЕ забывайте называть их точно так, как делает Omid, и НЕ забывайте сделать их исполняемыми. Если вы забудете об этом, они не будут иметь никакого эффекта, и вы запустите "git commit" снова и снова, задаваясь вопросом, почему ничего не происходит:) Также, если вы вырезаете и вставляете из веб-браузера, будьте осторожны, чтобы кавычки и тики не изменяются.

Если вы запустили pre-commit script один раз (запустив git commit), тогда будут созданы файлы .permissions. Вы можете добавить его в репозиторий, и я думаю, что нет необходимости добавлять его снова и снова в конце pre-commit script. Но это не больно, я думаю (надеюсь).

Существует несколько небольших проблем с именем каталога и наличием пробелов в именах файлов в сценариях Omid. Пространства были проблемой здесь, и у меня были некоторые проблемы с исправлением IFS. Для записи эта предварительная фиксация script корректно работала для меня:

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

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

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

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

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

Пространства в именах файлов являются проблемой обоих сценариев Omid. В post-checkout script вы узнаете, что у вас есть пробелы в именах файлов, если вы видите ошибки вроде этого

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

Ответ 5

В pre-commit/post-checkout будет использоваться функция "mtree" (FreeBSD) или "fmtree" (Ubuntu), которая "сравнивает иерархию файлов со спецификацией, создает спецификацию для иерархии файлов, или изменяет спецификацию."

По умолчанию установлены флаги, gid, link, mode, nlink, размер, время, тип и uid. Это может быть установлено для конкретной цели с помощью ключа -k.

Ответ 6

Я работаю на FreeBSD 11.1, концепция виртуализации freebsd jail делает операционную систему оптимальной. Текущая версия 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

Для файла .gitignore с 644 разрешениями, предоставленными для root: wheel

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

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

Ответ 7

Одно из добавлений к ответу @Omid Ariyan - права доступа к каталогам. Добавьте это после цикла for done в его скрипте pre-commit.

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

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

Ответ 8

Мы можем улучшить другие ответы, изменив формат файла .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 "[email protected]"