#!/bin/bash
# /usr/bin/git-dot JL 20170124
# This file is part of git-dot, which requires git and git-crypt.
license() { cat <
EOF
}
warn() { echo "$(basename $0):" "$@"; }
error() { warn "$@"; exit 1; }
GIT_DIR="$HOME/.git-dot" # Not .git !!!
tracked_files() {
git ls-tree -r master --name-only
}
encrypted_files() {
git crypt status | grep -v '^not' | awk '{print $2}'
}
plaintext_files() {
git crypt status | grep '^not' | awk '{print $3}'
}
# Kludge until proper support is provided
is_locked() {
if is_repo
then
grep -qsPa "\x00GITCRYPT" $(encrypted_files)
else
error "'locked' is not valid here"
fi
}
is_repo() {
git rev-parse --is-inside-work-tree &>/dev/null
}
can_run() { [[ ' init clone rev-parse ' =~ " $1 " ]] || is_repo; }
git() {
if can_run $1
then
#echo >&2 git --git-dir="$GIT_DIR" --work-tree="$HOME" "$@"
command git --git-dir="$GIT_DIR" --work-tree="$HOME" "$@"
else
error "'$1' is not valid here."
fi
}
write_exclude() {
cat <<-EOF > "$GIT_DIR/info/exclude"
*
!.*
!.*/**
*.un~
*.*~
$(basename "$GIT_DIR")
EOF
}
write_attributes() {
echo '.gitattributes !filter !diff' > "$HOME/.gitattributes"
}
init() {
(( $# == 0 )) || error "'$*' is not valid here."
is_repo && error "'$HOME' is already managed."
git init && git crypt init && write_exclude && write_attributes
}
encrypt() {
for path in "$@"
do
# format wildcards as required by .gitattributes
if [[ -d "$path" ]]
then
wildcard='/*'
elif [[ $(basename "$path") =~ \*\.* ]]
then
wildcard="/$(basename "$path")"
path="$(dirname "$path")"
fi
# add entry to .gitattributes if not already present
if [[ -e "$path" ]]
then
path+="$wildcard" # if any
if ! grep -qs "^${path//\*/\\*} " .gitattributes # escape '*' for regex!
then
echo "$path filter=git-crypt diff=git-crypt" >> "$HOME/.gitattributes" &&
added+=("$path")
fi
else
warn "'$path' not found."
fi
done
# Commit .gitattributes if changed
if (( ${#added[@]} > 0 ))
then
set -f # do not expand pathspecs in the commit message
msg=$(printf "Specify %d encrypted file%s\n%s\n" \
${#added[@]} \
"$((( ${#added[@]} == 1 )) || printf s)" \
"$(printf ' %s\n' "${added[@]}")" )
git add "$HOME/.gitattributes" &&
git commit -m "$msg" "$HOME/.gitattributes"
set +f
fi
}
clone() {
if (( $# > 1 ))
then
error "unexpected arguments: '$*'"
elif is_repo
then
error "'$HOME' is already managed."
else
git clone --bare "$1" "$GIT_DIR"
git config core.bare false
git config core.logallrefupdates true
git reset &> /dev/null
# Backup existing files that will be overwritten by checkout
backup=$(mktemp -p . -d git-dot-backup.XXXXXXXX.tmp)
tracked_files | xargs -I f cp -a --parents -l f "$backup" 2>/dev/null
git checkout .
git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
git config branch.master.remote origin
git config branch.master.merge refs/heads/master
git update-ref refs/remotes/origin/master HEAD@{0}
git config filter.git-crypt.smudge '"git-crypt" smudge'
git config filter.git-crypt.clean '"git-crypt" clean'
git config diff.git-crypt.textconv '"git-crypt" diff'
write_exclude
# consume 'Unable to open key file' messages
# https://github.com/AGWA/git-crypt/issues/108
sleep 1 && git status &> /dev/null
#Move plaintext modified files back into working tree
comm -12 <(plaintext_files | sort -u) \
<(cd "$backup"; find -type f -printf '%P\n' | sort -u) \
| ( cd "$backup"; xargs -I {} sh -c 'cp -a --parents -l -f {} "$HOME"; rm {}')
#Report pre-exisiting but now encrypted files that were moved out of the way
if [[ -n "$(ls -A "$backup")" ]]
then
echo -e "\nThe following files are now encrypted:"
find "$backup" -type f -printf '%P\n'
echo "The original files have been moved to '$backup'."
echo "Unlock the encrypted files before moving them back."
else
rm -rf "$backup"
fi
# Report unclean working directory which will prevent unlocking
if ! git diff-index --quiet HEAD --
then
echo -e '\nYour working directory is unclean. Please stash changes before unlocking.'
echo ' (use "git dot stash" before and "git dot stash pop" after unlocking)'
fi
fi
}
protect() {
encrypted_files | xargs chmod go-rwx
}
help() { cat <