Сохранение разрешений файлов с помощью 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]"