Beibehalten von Dateiberechtigungen mit Git

108

Ich möchte die Versionskontrolle meines Webservers wie unter Versionskontrolle für meinen Webserver beschrieben durchführen , indem ich aus meinem ein Git-Repo erstelle /var/www directory. Meine Hoffnung war, dass ich dann Webinhalte von unserem Entwicklungsserver auf Github übertragen, auf unseren Produktionsserver ziehen und den Rest des Tages am Pool verbringen kann.

Anscheinend ist ein Knick in meinem Plan, dass Git die Dateiberechtigungen nicht respektiert (ich habe es nicht ausprobiert, sondern erst jetzt darüber gelesen). Ich denke, dies ist insofern sinnvoll, als verschiedene Boxen unterschiedliche Benutzer- / Gruppen-Setups haben können. Wenn ich jedoch die Weitergabe von Berechtigungen erzwingen wollte und weiß, dass meine Server gleich konfiguriert sind, habe ich dann Optionen? Oder gibt es eine einfachere Möglichkeit, sich dem zu nähern, was ich versuche?

Yarin
quelle
1
Ja, denke schon, obwohl ich ehrlich gesagt nicht sicher bin, was ich damit anfangen soll. Ich hoffte auf einen einfacheren Ansatz.
Yarin
Was ist mit der Situation, in der der Quellcode aus einer Entwicklungsumgebung stammt (z. B. Windows - XAMPP usw.), die keine Informationen zum Dateieigentum enthält? Dateien am Ende des Git-Prozesses müssen mit dem Besitz und den Berechtigungen für den Zielspeicherort übereinstimmen. Kann Git-Cache-Meta damit umgehen? Stimmen Sie mit Yarin überein ... sicherlich ist dies ein ziemlich allgemeiner Anwendungsfall, der eine ziemlich einfache Lösung haben sollte?
user3600150

Antworten:

43

Die git-cache-metain SO erwähnte Frage " git - wie kann man die Dateiberechtigungen wiederherstellen, die git für die Datei hält? " (Und die git-FAQ ) ist der geradlinigere Ansatz.

Die Idee ist, .git_cache_metadie Berechtigungen der Dateien und Verzeichnisse in einer Datei zu speichern .
Es ist eine separate Datei, die nicht direkt im Git-Repo versioniert ist.

Deshalb ist die Verwendung dafür:

$ 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

Also du:

  • Bündeln Sie Ihr Repo und speichern Sie die zugehörigen Dateiberechtigungen.
  • Kopieren Sie diese beiden Dateien auf den Remote-Server
  • Stellen Sie dort das Repo wieder her und wenden Sie die Berechtigung an
VonC
quelle
2
VonC- Danke dafür, ich werde es ausprobieren- aber ist die Bündelung notwendig? Könnte ich meinen Workflow (dev -> github -> Produktion) nicht beibehalten und einfach die Metadatei ein- und auschecken?
Yarin
@ Yarin: Nein, das Bundle ist nicht obligatorisch. Es ist eine gute Möglichkeit, Repo zu übertragen, wenn jedoch kein anderes Übertragungsprotokoll verfügbar ist.
VonC
3
Die Verwendung von Bündeln hier war für mich eine große Ablenkung. Eigentlich hat mich die Antwort völlig abgeschreckt. (Ich habe keine Schwierigkeiten, Repo vom Server zu ziehen.) Die Antwort von @ omid-ariyan unten mit Pre / Post-Commit-Hooks war viel verständlicher. Später stellte ich fest, dass diese Hook-Skripte genau die gleiche Arbeit leisten wie Git-Cache-Meta. Sehen Sie, was ich meine: gist.github.com/andris9/1978266 . Sie analysieren und speichern die Rückkehr von git ls-files.
Pauljohn32
Der Link zu git-cache-meta ist tot - kann jemand, der davon weiß, ihn finden und den Beitrag bearbeiten?
Rosuav
@rosuav Sicher: Ich habe die Antwort bearbeitet und den Link wiederhergestellt. Vielen Dank, dass Sie mich über diesen toten Link informiert haben.
VonC
63

Git ist ein Versionskontrollsystem, das für die Softwareentwicklung entwickelt wurde. Aus allen Modi und Berechtigungen werden also nur ausführbare Bits (für normale Dateien) und Symlink-Bits gespeichert. Wenn Sie vollständige Berechtigungen speichern möchten, benötigen Sie Tools von Drittanbietern wie git-cache-meta( von VonC erwähnt ) oder Metastore (von etckeeper verwendet ). Oder Sie können IsiSetup verwenden , das IIRC git als Backend verwendet.

Siehe Seite Schnittstellen, Frontends und Tools im Git-Wiki.

Jakub Narębski
quelle
2
Danke Jakub - kannst du mir erklären, warum Git sich um das ausführbare Bit kümmert und nur darum?
Yarin
5
@ Yarin: nur ausführbares Bit? Wenn Sie alle Dateien von einem System auf ein anderes klonen, ist der Begriff "schreibgeschützt" oder "schreibgeschützt" nicht genau relevant (wie Sie in Ihrer Frage gesagt haben: verschiedene Benutzer / Gruppen). Der Begriff "ausführbar" hängt jedoch nicht von Benutzern und Gruppen ab und kann von System zu (Remote-) System wiederverwendet werden.
VonC
1
Jakub, in diesem Fall sollte es die Berechtigungen nicht ändern . Ich meine, es sollte Dauerwellen in Ruhe lassen oder sie verwalten, aber nicht mit ihnen herumspielen, wenn es sie nicht verwalten wird.
CommaToast
3
Außerdem habe ich /usr/share/git-core/contrib/hooks/setgitperms.perlin meinem git-contribPaket ein Skript für einen ähnlichen Zweck gefunden. ("Dieses Skript kann verwendet werden, um vollständige Berechtigungen und
Besitzdaten
Ist das noch richtig oder macht Github irgendwie etwas zusätzlich zu Git? Ich habe gerade eine Datei in eine ausführbare Datei geändert und sie festgeschrieben, und das Änderungsprotokoll für das Festschreiben wird als 0 Zeilen angezeigt, die für die Datei geändert wurden, aber neben dem Dateinamen steht 100644 → 100755. Dies sieht wirklich so aus, als ob die vollständigen Berechtigungen in der Datei gespeichert sind.
Cruncher
23

Dies ist ziemlich spät, könnte aber einigen anderen helfen. Ich mache, was du willst, indem ich meinem Repository zwei Git-Hooks hinzufüge.

.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 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 / post-checkout:

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

Der erste Hook wird aufgerufen, wenn Sie "festschreiben". Er liest den Besitz und die Berechtigungen für alle Dateien im Repository und speichert sie in einer Datei im Stammverzeichnis des Repositorys mit dem Namen .permissions. Fügen Sie dann die .permissions-Datei zum Festschreiben hinzu.

Der zweite Hook wird beim "Auschecken" aufgerufen und durchsucht die Liste der Dateien in der .permissions-Datei und stellt den Besitz und die Berechtigungen dieser Dateien wieder her.

  • Möglicherweise müssen Sie das Festschreiben und Auschecken mit sudo durchführen.
  • Stellen Sie sicher, dass die Skripte vor dem Festschreiben und nach dem Auschecken über Ausführungsberechtigungen verfügen.
Omid Ariyan
quelle
Omid ... danke! Ich fand Ihren Code eine perfekte Lösung für mich.
Ricalsin
@ Ricalsin Gern geschehen! Ich bin froh, geholfen zu haben :)
Omid Ariyan
1
$SELF_DIR/../../ist nicht unbedingt die Wurzel des Repositorys ... aber es git rev-parse --show-toplevelist. (Nicht sicher, warum Sie nicht nur pwdfür das aktuelle Verzeichnis verwenden würden, aber das ist sowieso
strittig
So wie es aussieht, werden die oben genannten Dateinamen mit Leerzeichen geteilt. Gemäß dieser Antwort können Sie IFS=$'\n'vor der forSchleife festlegen , dass dies gestoppt wird (und unset IFSdanach, um sicher zu gehen).
PJSCopeland
Auf diese Weise können Sie die Berechtigungen nicht ohne Weiteres auf ein anderes System mit einem anderen Betriebssystem übertragen, auf dem Sie einen anderen Benutzernamen haben. Ich fragte mich: "Was brauche ich wirklich ?" und schneiden Sie die gesamte Lösung auf chmod 0600 .pgpassin post-checkout. Ja, ich muss es manuell aktualisieren, wenn ich eine Datei habe, die bestimmte Berechtigungen benötigt, aber das sind die Unterbrechungen.
PJSCopeland
2

Für den Fall, dass Sie gerade darauf eingehen, habe ich es heute gerade durchgemacht und kann zusammenfassen, wo dies steht. Wenn Sie dies noch nicht versucht haben, können einige Details hier hilfreich sein.

Ich denke, @Omid Ariyans Ansatz ist der beste Weg. Fügen Sie die Skripte vor dem Festschreiben und nach dem Auschecken hinzu. Vergessen Sie nicht, sie genau so zu benennen, wie Omid es tut, und vergessen Sie nicht, sie ausführbar zu machen. Wenn Sie eines davon vergessen, haben sie keine Auswirkung und Sie führen immer wieder "git commit" aus und fragen sich, warum nichts passiert :) Achten Sie auch beim Ausschneiden und Einfügen aus dem Webbrowser darauf, dass die Anführungszeichen und Häkchen nicht vorhanden sind verändert.

Wenn Sie das Pre-Commit-Skript einmal ausführen (indem Sie ein Git-Commit ausführen), wird die Datei .permissions erstellt. Sie können es dem Repository hinzufügen, und ich denke, es ist nicht erforderlich, es am Ende des Pre-Commit-Skripts immer wieder hinzuzufügen. Aber es tut nicht weh, denke ich (Hoffnung).

Es gibt einige kleine Probleme mit dem Verzeichnisnamen und dem Vorhandensein von Leerzeichen in den Dateinamen in Omids Skripten. Die Leerzeichen waren hier ein Problem und ich hatte einige Probleme mit dem IFS-Fix. Für die Aufzeichnung hat dieses Pre-Commit-Skript für mich korrekt funktioniert:

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

Was haben wir davon?

Die .permissions-Datei befindet sich in der obersten Ebene des Git-Repos. Es hat eine Zeile pro Datei, hier ist der Anfang meines Beispiels:

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

Wie Sie sehen können, haben wir

filepath;perms;owner;group

In den Kommentaren zu diesem Ansatz beschwert sich eines der Poster, dass es nur mit demselben Benutzernamen funktioniert, und das ist technisch richtig, aber es ist sehr einfach, es zu beheben. Beachten Sie, dass das Post-Checkout-Skript zwei Aktionselemente enthält.

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

Also behalte ich nur den ersten, das ist alles was ich brauche. Mein Benutzername auf dem Webserver ist zwar anders, aber was noch wichtiger ist, Sie können chown nur ausführen, wenn Sie root sind. Kann jedoch "chgrp" ausführen. Es ist klar genug, wie man das benutzt.

In der ersten Antwort in diesem Beitrag, die am weitesten verbreitet ist, wird vorgeschlagen, git-cache-meta zu verwenden, ein Skript, das die gleiche Arbeit leistet wie die Pre / Post-Hook-Skripte hier (Analyse der Ausgabe von git ls-files). . Diese Skripte sind für mich leichter zu verstehen, der Git-Cache-Meta-Code ist etwas ausgefeilter. Es ist möglich, Git-Cache-Meta im Pfad zu belassen und Skripte vor dem Festschreiben und nach dem Auschecken zu schreiben, die es verwenden würden.

Leerzeichen in Dateinamen sind bei beiden Omid-Skripten ein Problem. Im Post-Checkout-Skript wissen Sie, dass Sie die Leerzeichen in Dateinamen haben, wenn Sie solche Fehler sehen

$ 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

Ich suche nach Lösungen dafür. Hier ist etwas, das zu funktionieren scheint, aber ich habe nur in einem Fall getestet

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

Da die Berechtigungsinformationen jeweils eine Zeile umfassen, setze ich IFS auf $, sodass nur Zeilenumbrüche als neue Dinge angesehen werden.

Ich habe gelesen, dass es SEHR WICHTIG ist, die IFS-Umgebungsvariable wieder so zu setzen, wie sie war! Sie können sehen, warum eine Shell-Sitzung möglicherweise schlecht verläuft, wenn Sie $ als einziges Trennzeichen belassen.

pauljohn32
quelle
2

Wir können die anderen Antworten verbessern, indem wir das Format der .permissionsDatei in ausführbare chmodAnweisungen ändern und den -printfParameter to verwenden find. Hier ist die einfachere .git/hooks/pre-commitDatei:

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

... und hier ist die vereinfachte .git/hooks/post-checkoutDatei:

#!/usr/bin/env bash

echo -n "Restoring file permissions... "

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

. .permissions

echo "done."

Denken Sie daran, dass andere Tools diese Skripts möglicherweise bereits konfiguriert haben, sodass Sie sie möglicherweise zusammenführen müssen. Hier ist beispielsweise ein post-checkoutSkript, das auch die folgenden git-lfsBefehle enthält:

#!/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 "$@"
Tammer Saleh
quelle
1

Beim Pre-Commit / Post-Checkout besteht die Option darin, das Dienstprogramm "mtree" (FreeBSD) oder "fmtree" (Ubuntu) zu verwenden, das "eine Dateihierarchie mit einer Spezifikation vergleicht, eine Spezifikation für eine Dateihierarchie erstellt oder a ändert Spezifikation."

Die Standardeinstellungen sind Flags, GID, Link, Modus, N-Link, Größe, Zeit, Typ und UID. Dies kann mit dem Schalter -k an den jeweiligen Zweck angepasst werden.

Vladimir Botka
quelle
1

Ich verwende FreeBSD 11.1. Das Virtualisierungskonzept von freebsd jail macht das Betriebssystem optimal. Die aktuelle Version von Git, die ich verwende, ist 2.15.1. Ich bevorzuge es auch, alles auf Shell-Skripten auszuführen. In diesem Sinne habe ich die obigen Vorschläge wie folgt geändert:

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;

Wenn Sie das Skript aus irgendeinem Grund neu erstellen müssen, sollte die Ausgabe der .permissions-Datei das folgende Format haben:

.gitignore;644;0;0

Für eine Gitignore-Datei mit 644 Berechtigungen für root: rad

Beachten Sie, dass ich einige Änderungen an den Statistikoptionen vornehmen musste.

Genießen,

Albaro Pereyra
quelle
1

Eine Ergänzung zu @Omid Ariyans Antwort sind Berechtigungen für Verzeichnisse. Fügen Sie dies nach der forSchleife donein seinem pre-commitSkript hinzu.

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

Dadurch werden auch Verzeichnisberechtigungen gespeichert.

Peter Berbec
quelle