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