summaryrefslogtreecommitdiff
path: root/pre-commit.d/30store-metadata
diff options
context:
space:
mode:
Diffstat (limited to 'pre-commit.d/30store-metadata')
-rwxr-xr-xpre-commit.d/30store-metadata144
1 files changed, 144 insertions, 0 deletions
diff --git a/pre-commit.d/30store-metadata b/pre-commit.d/30store-metadata
new file mode 100755
index 0000000..03ce475
--- /dev/null
+++ b/pre-commit.d/30store-metadata
@@ -0,0 +1,144 @@
+#!/bin/sh
+set -e
+
+# Filters out UNKNOWN users and groups, prints a warning on stderr.
+filter_unknown() {
+ CMD=$1
+ while read line; do
+ # if the first n chars of $line equal "$CMD UNKNOWN "...
+ if [ "$(printf %.$((9+${#CMD}))s "$line")" = "$CMD UNKNOWN " ]; then
+ echo Bad "$2" for "$line" >&2
+ else
+ echo "$line"
+ fi
+ done
+}
+
+filter_ignore() {
+ if [ "$VCS" = darcs ]; then
+ ignorefile=.darcsignore
+ fi
+
+ if [ "$VCS" = darcs ] && [ -e "$ignorefile" ]; then
+ patternsfile="$( mktemp -t etckeeper-$VCS.XXXXXXXXXX )"
+ grep -v '^[[:space:]]*\(#\|$\)' "$ignorefile" > "$patternsfile" || true
+ grep -Evf "$patternsfile"
+ rm -f "$patternsfile"
+ unset patternsfile
+ else
+ cat -
+ fi
+}
+
+shellquote() {
+ # Single quotes text, escaping existing single quotes.
+ sed -e "s/'/'\"'\"'/g" -e "s/^/'/" -e "s/$/'/"
+}
+
+generate_metadata() {
+ # This function generates the script commands to fix any file
+ # ownerships that aren't owner=root, group=root, as well as to
+ # store the permissions of files.
+ # The script is produced on stdout. Errors go to stderr.
+ #
+ # The script can use a 'maybe' function, which only runs a command
+ # if the file in its last argument exists.
+
+ # We want files in the directory containing VCS data
+ # but we want find to ignore the VCS files themselves.
+ #
+ # (Note that when using this, the find expression must end with
+ # -print or -exec, else the excluded directories will actually be
+ # printed!)
+ NOVCS='. -path ./.git -prune -o -path ./.bzr -prune -o -path ./.hg -prune -o -path ./_darcs -prune -o'
+
+ # Keep the sort order the same at all times.
+ LC_COLLATE=C
+ export LC_COLLATE
+
+ if [ "$VCS" = git ] || [ "$VCS" = hg ]; then
+ # These version control systems do not track directories,
+ # so empty directories must be stored specially.
+ find $NOVCS -type d -empty -print |
+ sort | shellquote | sed -e "s/^/mkdir -p /"
+ fi
+
+ if [ "$VCS" = darcs ]; then
+ # This version control system does not track symlinks,
+ # so they must be stored specially.
+ find $NOVCS -type l -print | sort | filter_ignore | while read link; do
+ dest=$( readlink "$link" )
+ printf "ln -sf '%s' '%s'\n" "$(echo "$dest" | shellquote)" "$(echo "$link" | shellquote)"
+ done
+ fi
+
+ # Store things that don't have the default user or group.
+ # Store all file modes, in case the user has an unusual umask.
+ find $NOVCS \( -type f -or -type d \) -print | sort | perl -ne '
+ BEGIN { $q=chr(39) }
+ sub uidname {
+ my $want=shift;
+ if (exists $uidcache{$want}) {
+ return $uidcache{$want};
+ }
+ my $name=scalar getpwuid($want);
+ return $uidcache{$want}=defined $name ? $name : $want;
+ }
+ sub gidname {
+ my $want=shift;
+ if (exists $gidcache{$want}) {
+ return $gidcache{$want};
+ }
+ my $name=scalar getgrgid($want);
+ return $gidcache{$want}=defined $name ? $name : $want;
+ }
+ chomp;
+ my @stat=stat($_);
+ my $mode = $stat[2];
+ my $uid = $stat[4];
+ my $gid = $stat[5];
+ s/$q/$q"$q"$q/g; # escape single quotes
+ s/^/$q/;
+ s/$/$q/;
+ if ($uid != $>) {
+ printf "maybe chown $q%s$q %s\n", uidname($uid), $_;
+ }
+ if ($gid != $)) {
+ printf "maybe chgrp $q%s$q %s\n", gidname($gid), $_;
+ }
+ printf "maybe chmod %04o %s\n", $mode & 07777, $_;
+ '
+
+ # We don't handle xattrs.
+ # Maybe check for getfattr/setfattr and use them if they're available?
+}
+
+if [ "$VCS" = git ] || [ "$VCS" = hg ] || [ "$VCS" = bzr ] || [ "$VCS" = darcs ]; then
+ if [ -f .metadata ]; then
+ # remove obsolete .metadata file
+ # git allows fully deleting it at this point, other VCS
+ # may not (the repo is locked for hg).
+ if [ "$VCS" = git ]; then
+ $VCS rm .metadata
+ else
+ rm -f .metadata
+ fi
+ fi
+
+ echo "# Generated by etckeeper. Do not edit." > .etckeeper
+ echo >> .etckeeper
+
+ # Make sure the file is not readable by others, since it can leak
+ # information about contents of non-readable directories in /etc.
+ chmod 700 .etckeeper
+
+ generate_metadata >> .etckeeper
+
+ # stage the file as part of the current commit
+ if [ "$VCS" = git ]; then
+ # this will do nothing if the metadata file is unchanged.
+ git add .etckeeper
+ fi
+ # hg, bzr and darcs add not done, they will automatically
+ # include the file in the current commit
+fi