Conserver les permissions de fichiers avec Git

Je souhaite contrôler la version de mon serveur Web, comme décrit dans la section Contrôle de la version pour mon serveur Web , en créant un repository git à partir de mon /var/www directory . J’espérais pouvoir ensuite envoyer du contenu Web de notre serveur de développement vers github, le transférer vers notre serveur de production et passer le rest de la journée au pool.

Apparemment, un pépin dans mon plan est que Git ne respectera pas les permissions de fichiers (je ne l’ai pas essayé, je ne l’ai lu que maintenant). Je suppose que cela a du sens dans la mesure où les différentes boîtes sont susceptibles d’avoir des configurations utilisateur / groupe différentes. 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 existe-t-il un moyen plus facile d’aborder ce que j’essaie de faire?

Le git-cache-meta mentionné dans la question SO ” git – comment récupérer les permissions de fichier que git pense que le fichier devrait être (et la FAQ de git ) est l’approche la plus simple.

L’idée est de stocker dans un fichier .git_cache_meta les permissions des fichiers et des répertoires.
C’est un fichier séparé non versionné directement dans le repository Git.

C’est pourquoi l’utilisation est la suivante:

 $ 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 

Donc vous:

  • regroupez votre repository et enregistrez les permissions de fichier associées.
  • copier ces deux fichiers sur le serveur distant
  • restaurer le repo là et appliquer la permission

Git est un système de contrôle de version, créé pour le développement de logiciels. Ainsi, à partir de l’ensemble des modes et des permissions, il ne stocke que le bit exécutable (pour les fichiers ordinaires) et le bit de lien symbolique. Si vous souhaitez stocker des permissions complètes, vous avez besoin d’un outil tiers, tel que git-cache-meta ( mentionné par VonC ) ou Metastore (utilisé par etckeeper ). Ou vous pouvez utiliser IsiSetup , que IIRC utilise git comme backend.

Voir la page Interfaces, frontends et tools sur le wiki Git.

C’est assez tard mais cela pourrait aider d’autres. Je fais ce que vous voulez en ajoutant deux hooks git à mon référentiel.

.git / hooks / pre-commit:

 #!/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 file permissions..." IFS_OLD=$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=$IFS_OLD # Add the permissions database file to the index git add $DATABASE echo "OK" 

.git / hooks / post-checkout:

 #!/bin/bash SELF_DIR=`git rev-parse --show-toplevel` DATABASE=$SELF_DIR/.permissions echo -n "Restoring file permissions..." IFS_OLD=$IFS; IFS=$'\n' 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=$IFS_OLD echo "OK" exit 0 

Le premier hook est appelé lorsque vous "validez" et lit les droits de propriété et les permissions pour tous les fichiers du référentiel et les stocke dans un fichier à la racine du référentiel appelé .permissions, puis ajoute le fichier .permissions au commit.

Le deuxième crochet est appelé lorsque vous "récupérez" et parcourra la liste des fichiers du fichier .permissions et restaure la propriété et les permissions de ces fichiers.

  • Vous devrez peut-être faire la validation et la validation en utilisant sudo.
  • Assurez-vous que les scripts de pré-validation et de post-extraction ont une autorisation d'exécution.

Au cas où vous aborderiez cela maintenant, je viens juste de le passer aujourd’hui et je peux résumer la situation. Si vous ne l’avez pas encore essayé, certains détails pourraient vous aider.

Je pense que l’approche de @Omid Ariyan est la meilleure façon. Ajoutez les scripts de pré-validation 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 ou l’autre, ils n’ont aucun effet et vous lancez “git commit” sans cesse en vous demandant pourquoi rien ne se passe 🙂 En outre, si vous coupez et collez le navigateur Web, veillez à ne pas utiliser les guillemets modifié.

Si vous exécutez le script pre-commit une fois (en exécutant un commit git), le fichier .permissions sera créé. Vous pouvez l’append au référentiel et je pense qu’il est inutile de l’append encore et encore à la fin du script de pré-validation. Mais ça ne fait pas mal, je pense (espoir).

Il y a quelques petits problèmes concernant le nom du répertoire et l’existence d’espaces dans les noms de fichiers dans les scripts Omid. Les espaces étaient un problème ici et j’ai eu des problèmes avec le correctif IFS. Pour mémoire, ce script de pré-validation fonctionnait correctement pour moi:

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

Maintenant, qu’est-ce que nous en sortons?

Le fichier .permissions se trouve au niveau supérieur du repository git. Il a une ligne par fichier, voici le haut 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

 filepath;perms;owner;group 

Dans les commentaires sur cette approche, l’une des affiches se plaint qu’elle ne fonctionne qu’avec le même nom d’utilisateur, ce qui est techniquement vrai, mais il est très facile de le réparer. Notez que le script post-extraction a 2 actions,

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

Donc, je ne garde que le premier, c’est tout ce dont j’ai besoin. Mon nom d’utilisateur sur le serveur Web est différent, mais plus important encore, vous ne pouvez pas exécuter chown à moins d’être root. Peut exécuter “chgrp”, cependant. Il est assez clair de savoir comment l’utiliser.

Dans la première réponse de cet article, celle qui est la plus largement acceptée, la suggestion est donc d’utiliser git-cache-meta, un script qui effectue le même travail que les scripts pre / post hook (parsing des résultats de git ls-files ). Ces scripts sont plus faciles à comprendre pour moi, le code git-cache-meta est plutôt plus élaboré. Il est possible de garder git-cache-meta dans le chemin et d’écrire des scripts de pré-validation et de post-extraction qui l’utilisent.

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

 $ 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 

Je vérifie les solutions pour 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 "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 

Comme les informations sur les permissions sont une ligne à la fois, je définis IFS sur $, de sorte que seuls les sauts de ligne sont considérés comme de nouveaux éléments.

J'ai lu qu'il est TRES IMPORTANT de redéfinir la variable d'environnement IFS comme elle était! Vous pouvez voir pourquoi une session shell peut mal se passer si vous laissez $ comme seul séparateur.

Dans pre-commit / post-checkout, une option serait d’utiliser “mtree” (FreeBSD), ou “fmtree” (Ubuntu) utilitaire qui “compare une hiérarchie de fichiers à une spécification, crée une spécification pour une hiérarchie de fichiers ou modifie une spécification.”

L’ensemble par défaut est les indicateurs, gid, link, mode, nlink, size, time, type et uid. Cela peut être adapté à l’objective spécifique avec l’option -k.

Je tourne sous FreeBSD 11.1, le concept de virtualisation freebsd jail rend le système d’exploitation optimal. La version actuelle de Git que j’utilise est la 2.15.1, je préfère également tout exécuter sur les scripts shell. Dans cet esprit, j’ai modifié les suggestions ci-dessus comme suit:

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-fusion

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

Si pour une raison quelconque vous devez recréer le script, la sortie du fichier .permissions doit avoir le format suivant:

 .gitignore;644;0;0 

Pour un fichier .gitignore avec 644 permissions accordées à root: wheel

Notez que j'ai dû apporter quelques modifications aux options de statistiques.

Prendre plaisir,

Un ajout à la réponse de @Omid Ariyan concerne les permissions sur les répertoires. Ajoutez ceci après la boucle for dans son script de 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 

Cela permettra également de sauvegarder les permissions de répertoire.