112 votes

Conserver les permissions de fichier avec Git

Je veux contrôler la version de mon serveur web comme décrit dans Contrôle de version pour mon serveur web, en créant un dépôt git à partir de mon répertoire /var/www. Mon espoir était alors de pouvoir pousser le contenu web depuis notre serveur de développement vers github, le tirer vers notre serveur de production, et passer le reste de la journée à la piscine.

Apparemment, un hic dans mon plan est que Git ne respecte pas les permissions des fichiers (je ne l'ai pas encore essayé, je lis à ce sujet maintenant.) Je suppose que cela a du sens étant donné que différentes machines ont tendance à avoir différentes configurations utilisateurs/groupes. Mais si je voulais forcer les permissions à se propager, sachant que mes serveurs sont configurés de la même manière, ai-je des options ? Ou y a-t-il un moyen plus simple d'aborder ce que j'essaie de faire ?

1 votes

Ouais, je suppose, bien que la solution à laquelle ils font référence, franchement, je ne suis pas sûr de ce que je dois en faire. J'espérais une approche plus directe.

0 votes

Que dire de la situation où le code source provient de l'environnement de développement (par exemple Windows - XAMPP, etc.) qui n'a pas d'informations sur la propriété des fichiers? Les fichiers à la fin du processus git doivent correspondre à la propriété et aux autorisations de l'emplacement cible. Git-cache-meta peut-il gérer cela? D'accord avec Yarin ... sûrement il s'agit d'un cas d'utilisation assez classique, qui devrait avoir une solution assez simple?

66voto

Jakub Narębski Points 87537

Git est un système de contrôle de version, créé pour le développement logiciel, donc à partir de l'ensemble des modes et autorisations, il ne stocke que le bit exécutable (pour les fichiers ordinaires) et le bit symlink. Si vous souhaitez stocker toutes les autorisations, vous avez besoin d'un outil tiers, comme git-cache-meta (mentionné par VonC), ou Metastore (utilisé par etckeeper). Vous pouvez également utiliser IsiSetup, qui utilise je crois git en backend.

Voir la page Interfaces, frontaux et outils sur le Wiki Git.

2 votes

Merci Jakub - pouvez-vous m'expliquer pourquoi Git se soucie du bit exécutable et seulement de cela?

5 votes

@Yarin: seul le bit exécutable? Lorsque vous clonez un ensemble complet de fichiers d'un système à un autre, la notion de "lecture seule" ou "lecture-écriture" n'est pas exactement pertinente (comme vous l'avez dit dans votre question: différents utilisateurs/groupes). Cependant, la notion d'"exécutable" ne dépend pas des utilisateurs et des groupes et peut être réutilisée d'un système à un autre (à distance).

1 votes

Jakub, dans ce cas, cela ne devrait pas changer les autorisations. Je veux dire, cela devrait laisser les autorisations telles quelles ou les gérer, mais ne pas les perturber si elles ne sont pas gérées.

44voto

VonC Points 414372

Le git-cache-meta mentionné dans la question SO "git - how to recover the file permissions git thinks the file should be?" (et la git FAQ) est l'approche la plus directe.

L'idée est de stocker dans un fichier .git_cache_meta les permissions des fichiers et répertoires.
Il s'agit d'un fichier distinct non versionné directement dans le dépôt Git.

C'est pourquoi l'utilisation en est la suivante :

$ git bundle create mybundle.bdl master; git-cache-meta --store
$ scp mybundle.bdl .git_cache_meta machine2: 
#puis sur machine2:
$ git init; git pull mybundle.bdl master; git-cache-meta --apply

Donc, vous :

  • faites une bundle de votre dépôt et sauvegardez les permissions de fichier associées.
  • copiez ces deux fichiers sur le serveur distant
  • restaurez le dépôt là-bas, et appliquez les permissions

2 votes

VonC- Merci pour cela, je vais l'essayer- mais est l'emballage nécessaire? Ne pourrais-je pas conserver mon flux de travail (dev -> github -> production) et simplement vérifier/récupérer le métafichier?

0 votes

@Yarin : non, le bundle n'est pas obligatoire. C'est une façon pratique de transférer un dépôt lorsque aucun autre protocole de transfert n'est disponible cependant.

5 votes

L'utilisation du bundle ici a été une grande distraction pour moi. Cela m'a complètement désorienté, en fait. (Je n'ai pas de difficulté à extraire le repo depuis le serveur.) La réponse de @omid-ariyan avec les hooks pre/post commit ci-dessous était beaucoup plus compréhensible. Plus tard, j'ai réalisé que ces scripts hook font exactement le même travail que git-cache-meta. Allez voir ce que je veux dire: gist.github.com/andris9/1978266. Ils analysent et stockent le retour de git ls-files.

27voto

Omid Ariyan Points 350

Ceci est assez tard mais pourrait aider certaines personnes. Je fais ce que vous voulez faire en ajoutant deux hooks git à mon dépôt.

.git/hooks/pre-commit:

#!/bin/bash
#
# Un script de crochet appelé par "git commit" sans arguments. Le crochet devrait
# sortir avec un statut différent de zéro après avoir émis un message approprié s'il souhaite
# arrêter le commit.

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

# Efface le fichier de base de données de permissions
> $DATABASE

echo -n "Sauvegarde des permissions..."

IFS_OLD=$IFS; IFS=$'\n'
for FILE in `git ls-files --full-name`
do
   # Enregistrer les permissions de tous les fichiers dans l'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
   # Enregistrer les permissions de tous les répertoires dans l'index
   echo $DIRECTORY";"`stat -c "%a;%U;%G" $DIRECTORY` >> $DATABASE
done
IFS=$IFS_OLD

# Ajouter le fichier de base de données de permissions à l'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 "Restauration des 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`

   # Définir les permissions du fichier/répertoire
   chmod $PERMISSIONS $ITEM

   # Définir le propriétaire et le groupe du fichier/répertoire
   chown $USER:$GROUP $ITEM

done < $DATABASE
IFS=$IFS_OLD

echo "OK"

exit 0

Le premier crochet est appelé lorsque vous "commit" et lira la propriété et les permissions de tous les fichiers du dépôt et les stockera dans un fichier à la racine du dépôt appelé .permissions puis ajoutera le fichier .permissions au commit.

Le deuxième crochet est appelé lorsque vous "checkout" et parcourra la liste des fichiers dans le fichier .permissions et restaurera la propriété et les permissions de ces fichiers.

  • Vous pourriez avoir besoin de faire le commit et le checkout en utilisant sudo.
  • Assurez-vous que les scripts pre-commit et post-checkout ont l'autorisation d'exécution.

0 votes

Omid ... merci! J'ai trouvé ton code être une solution parfaite pour moi.

0 votes

@Ricalsin Vous êtes les bienvenus! Je suis content d'avoir pu aider :)

1 votes

$SELF_DIR/../../ n'est pas nécessairement la racine du dépôt... mais git rev-parse --show-toplevel l'est. (Pas sûr pourquoi vous ne viendriez pas simplement utiliser pwd pour le répertoire actuel, mais de toute façon, c'est une question de détail.)

5voto

Tammer Saleh Points 83

Nous pouvons améliorer les autres réponses en changeant le format du fichier .permissions pour qu'il contienne des déclarations chmod exécutables, et en utilisant le paramètre -printf pour find. Voici le fichier .git/hooks/pre-commit plus simple :

#!/usr/bin/env bash

echo -n "Sauvegarde des autorisations de fichiers... "

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

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

git add .permissions

echo done.

...et voici le fichier simplifié .git/hooks/post-checkout :

#!/usr/bin/env bash

echo -n "Restauration des autorisations de fichiers... "

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

. .permissions

echo "done."

N'oubliez pas que d'autres outils ont peut-être déjà configuré ces scripts, vous devrez donc peut-être les fusionner. Par exemple, voici un script post-checkout qui inclut également les commandes git-lfs :

#!/usr/bin/env bash

echo -n "Restauration des autorisations de fichiers... "

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

. .permissions

echo "done."

command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nCe dépôt est configuré pour Git LFS mais 'git-lfs' n'a pas été trouvé sur votre chemin. Si vous ne souhaitez plus utiliser Git LFS, supprimez ce hook en supprimant .git/hooks/post-checkout.\n"; exit 2; }
git lfs post-checkout "$@"

0 votes

Je trouve vraiment que l'idée de faire en sorte que le crochet pre-commit génère un script pour simplifier grandement le crochet post-checkout est géniale. Cependant, je pense que ce crochet pre-commit est un peu trop simple; il inclura tous les fichiers de l'arborescence, pas seulement ceux qui ont été commités dans le dépôt Git. En d'autres termes, il inclura tout dans .git/, les fichiers de .gitignore, les fichiers temporaires non commités, etc.

2voto

user1086346 Points 1

Si vous arrivez sur cette page maintenant, je viens juste de passer par cela aujourd'hui et je peux résumer où j'en suis. Si vous n'avez pas encore essayé cela, quelques détails ici pourraient aider.

Je pense que l'approche de @Omid Ariyan est la meilleure façon de procéder. Ajoutez les scripts de pré-engagement et de post-extraction. N'OUBLIEZ pas de les nommer exactement comme le fait Omid et N'OUBLIEZ pas de les rendre exécutables. Si vous oubliez l'un des deux, ils n'auront aucun effet et vous exécuterez "git commit" en vous demandant pourquoi rien ne se passe :) De plus, si vous copiez-collez à partir du navigateur web, faites attention à ce que les guillemets et les guillemets simples ne soient pas altérés.

Si vous exécutez le script de pré-engagement une fois (en exécutant un git commit), alors le fichier .permissions sera créé. Vous pouvez l'ajouter au dépôt et je pense qu'il est inutile de l'ajouter encore et encore à la fin du script de pré-engagement. Mais cela ne fait pas de mal, je pense (j'espère).

Il y a quelques petites questions concernant le nom du répertoire et l'existence d'espaces dans les noms de fichiers dans les scripts d'Omid. Les espaces posaient problème ici et j'ai eu des difficultés avec la correction de l'IFS. Pour mémoire, ce script de pré-engagement a fonctionné correctement pour moi :

#!/bin/bash  

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

# Effacer le fichier de base de données des permissions
> $DATABASE

echo -n "Sauvegarde des permissions des fichiers..."

IFSold=$IFS
IFS=$'\n'
for FILE  in `git ls-files`
do
   # Sauvegarder les permissions de tous les fichiers dans l'index
   echo $FILE";"`stat -c "%a;%U;%G" $FILE` >> $DATABASE
done
IFS=${IFSold}
# Ajouter le fichier de base de données des permissions à l'index
git add $DATABASE

echo "OK"

Maintenant, qu'est-ce que nous obtenons de tout cela ?

Le fichier .permissions se trouve dans le répertoire principal du dépôt git. Il a une ligne par fichier, voici le début de mon exemple :

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

Comme vous pouvez le voir, nous avons

chemin_d'accès;permissions;propriétaire;groupe

Dans les commentaires sur cette approche, l'un des contributeurs se plaint que cela ne fonctionne qu'avec le même nom d'utilisateur, et c'est techniquement vrai, mais c'est très facile à corriger. Notez que le script post-checkout a 2 parties d'action,

# Définir les permissions du fichier
chmod $PERMISSIONS $FILE
# Définir le propriétaire et le groupe du fichier
chown $USER:$GROUP $FILE

Je garde seulement la première, c'est tout ce dont j'ai besoin. Mon nom d'utilisateur sur le serveur Web est en effet différent, mais plus important encore, vous ne pouvez pas exécuter chown à moins d'être root. Vous pouvez exécuter "chgrp", cependant. Il est assez simple de comprendre comment l'utiliser.

Dans la première réponse de ce post, celle qui est la plus largement acceptée, la suggestion est d'utiliser git-cache-meta, un script qui fait le même travail que les scripts de crochet pre/post ici (analyse de la sortie de git ls-files). Ces scripts sont plus faciles pour moi à comprendre, le code git-cache-meta est plutôt plus élaboré. Il est possible de conserver git-cache-meta dans le chemin et d'écrire des scripts de pré-engagement et de post-extraction qui l'utiliseraient.

Les espaces dans les noms de fichiers posent problème avec les deux scripts d'Omid. Dans le script de post-extraction, vous saurez que vous avez des espaces dans les noms de fichiers si vous voyez des erreurs comme celle-ci

$ git checkout -- upload.sh
Restauration des permissions des fichiers...chmod: impossible d'accéder à '04.StartingValuesInLISREL/Open' : Aucun fichier ou dossier de ce type
chmod: impossible d'accéder à 'Notebook.onetoc2' : Aucun fichier ou dossier de ce type
chown: impossible d'accéder à '04.StartingValuesInLISREL/Open' : Aucun fichier ou dossier de ce type
chown: impossible d'accéder à 'Notebook.onetoc2' : Aucun fichier ou dossier de ce type

Je cherche des solutions à cela. Voici quelque chose qui semble fonctionner, mais je n'ai testé que dans un cas

#!/bin/bash

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

echo -n "Restauration des permissions des fichiers..."
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`

   # Définir les permissions du fichier
   chmod $PERMISSIONS $FILE
   # Définir le propriétaire et le groupe du fichier
   chown $USER:$GROUP $FILE
done < $DATABASE
IFS=${IFSold}
echo "OK"

exit 0

Comme les informations sur les permissions sont une ligne à la fois, j'ai défini IFS sur $, de sorte que seules les sauts de ligne sont considérés comme de nouvelles choses.

J'ai lu qu'il est TRÈS IMPORTANT de rétablir la variable d'environnement IFS comme elle était ! Vous pouvez voir pourquoi une session shell pourrait mal tourner si vous laissez $ comme seul séparateur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X