From b24a90c8dde57e019b7c88039821c4a39361f1b3 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sat, 25 Feb 2017 16:56:58 +0100 Subject: git: use inner-most directory --- man/pass.1 | 11 ++++----- src/password-store.sh | 64 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/man/pass.1 b/man/pass.1 index ffbc405..e842178 100644 --- a/man/pass.1 +++ b/man/pass.1 @@ -41,8 +41,10 @@ Otherwise COMMAND must be one of the valid commands listed below. Several of the commands below rely on or provide additional functionality if the password store directory is also a git repository. If the password store directory is a git repository, all password store modification commands will -cause a corresponding git commit. See the \fIEXTENDED GIT EXAMPLE\fP section -for a detailed description using \fBinit\fP and +cause a corresponding git commit. Sub-directories may be separate nested git +repositories, and pass will use the inner-most directory relative to the +current password. See the \fIEXTENDED GIT EXAMPLE\fP section for a detailed +description using \fBinit\fP and .BR git (1). The \fBinit\fP command must be run before other commands in order to initialize @@ -410,11 +412,6 @@ Overrides the default gpg key identification set by \fBinit\fP. Keys must not contain spaces and thus use of the hexadecimal key signature is recommended. Multiple keys may be specified separated by spaces. .TP -.I PASSWORD_STORE_GIT -Overrides the default root of the git repository, which is helpful if -\fIPASSWORD_STORE_DIR\fP is temporarily set to a sub-directory of the default -password store. -.TP .I PASSWORD_STORE_GPG_OPTS Additional options to be passed to all invocations of GPG. .TP diff --git a/src/password-store.sh b/src/password-store.sh index 17142f0..eced113 100755 --- a/src/password-store.sh +++ b/src/password-store.sh @@ -20,24 +20,30 @@ GENERATED_LENGTH="${PASSWORD_STORE_GENERATED_LENGTH:-25}" CHARACTER_SET="${PASSWORD_STORE_CHARACTER_SET:-[:graph:]}" CHARACTER_SET_NO_SYMBOLS="${PASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS:-[:alnum:]}" -export GIT_DIR="${PASSWORD_STORE_GIT:-$PREFIX}/.git" -export GIT_WORK_TREE="${PASSWORD_STORE_GIT:-$PREFIX}" +export GIT_CEILING_DIRECTORIES="$PREFIX/.." # # BEGIN helper functions # +set_git() { + INNER_GIT_DIR="${1%/*}" + while [[ ! -d $INNER_GIT_DIR && ${INNER_GIT_DIR%/*}/ == "${PREFIX%/}/"* ]]; do + INNER_GIT_DIR="${INNER_GIT_DIR%/*}" + done + [[ $(git -C "$INNER_GIT_DIR" rev-parse --is-inside-work-tree 2>/dev/null) == true ]] || INNER_GIT_DIR="" +} git_add_file() { - [[ -e $GIT_DIR ]] || return - git add "$1" || return - [[ -n $(git status --porcelain "$1") ]] || return + [[ -n $INNER_GIT_DIR ]] || return + git -C "$INNER_GIT_DIR" add "$1" || return + [[ -n $(git -C "$INNER_GIT_DIR" status --porcelain "$1") ]] || return git_commit "$2" } git_commit() { local sign="" - [[ -e $GIT_DIR ]] || return - [[ $(git config --bool --get pass.signcommits) == "true" ]] && sign="-S" - git commit $sign -m "$1" + [[ -n $INNER_GIT_DIR ]] || return + [[ $(git -C "$INNER_GIT_DIR" config --bool --get pass.signcommits) == "true" ]] && sign="-S" + git -C "$INNER_GIT_DIR" commit $sign -m "$1" } yesno() { [[ -t 0 ]] || return 0 @@ -306,12 +312,13 @@ cmd_init() { [[ -n $id_path && ! -d $PREFIX/$id_path && -e $PREFIX/$id_path ]] && die "Error: $PREFIX/$id_path exists but is not a directory." local gpg_id="$PREFIX/$id_path/.gpg-id" + set_git "$gpg_id" if [[ $# -eq 1 && -z $1 ]]; then [[ ! -f "$gpg_id" ]] && die "Error: $gpg_id does not exist and so cannot be removed." rm -v -f "$gpg_id" || exit 1 - if [[ -e $GIT_DIR ]]; then - git rm -qr "$gpg_id" + if [[ -n $INNER_GIT_DIR ]]; then + git -C "$INNER_GIT_DIR" rm -qr "$gpg_id" git_commit "Deinitialize ${gpg_id}${id_path:+ ($id_path)}." fi rmdir -p "${gpg_id%/*}" 2>/dev/null @@ -419,6 +426,7 @@ cmd_insert() { local path="${1%/}" local passfile="$PREFIX/$path.gpg" check_sneaky_paths "$path" + set_git "$passfile" [[ $force -eq 0 && -e $passfile ]] && yesno "An entry already exists for $path. Overwrite it?" @@ -459,6 +467,7 @@ cmd_edit() { mkdir -p -v "$PREFIX/$(dirname "$path")" set_gpg_recipients "$(dirname "$path")" local passfile="$PREFIX/$path.gpg" + set_git "$passfile" tmpdir #Defines $SECURE_TMPDIR local tmp_file="$(mktemp -u "$SECURE_TMPDIR/XXXXXX")-${path//\//-}.txt" @@ -500,6 +509,7 @@ cmd_generate() { mkdir -p -v "$PREFIX/$(dirname "$path")" set_gpg_recipients "$(dirname "$path")" local passfile="$PREFIX/$path.gpg" + set_git "$passfile" [[ $inplace -eq 0 && $force -eq 0 && -e $passfile ]] && yesno "An entry already exists for $path. Overwrite it?" @@ -545,14 +555,17 @@ cmd_delete() { local passdir="$PREFIX/${path%/}" local passfile="$PREFIX/$path.gpg" - [[ -f $passfile && -d $passdir && $path == */ || ! -f $passfile ]] && passfile="$passdir" + [[ -f $passfile && -d $passdir && $path == */ || ! -f $passfile ]] && passfile="${passdir%/}/" [[ -e $passfile ]] || die "Error: $path is not in the password store." + set_git "$passfile" [[ $force -eq 1 ]] || yesno "Are you sure you would like to delete $path?" rm $recursive -f -v "$passfile" - if [[ -e $GIT_DIR && ! -e $passfile ]]; then - git rm -qr "$passfile" + set_git "$passfile" + if [[ -n $INNER_GIT_DIR && ! -e $passfile ]]; then + git -C "$INNER_GIT_DIR" rm -qr "$passfile" + set_git "$passfile" git_commit "Remove $path from store." fi rmdir -p "${passfile%/*}" 2>/dev/null @@ -588,14 +601,23 @@ cmd_copy_move() { local interactive="-i" [[ ! -t 0 || $force -eq 1 ]] && interactive="-f" + set_git "$new_path" if [[ $move -eq 1 ]]; then mv $interactive -v "$old_path" "$new_path" || exit 1 [[ -e "$new_path" ]] && reencrypt_path "$new_path" - if [[ -e $GIT_DIR && ! -e $old_path ]]; then - git rm -qr "$old_path" + set_git "$new_path" + if [[ -n $INNER_GIT_DIR && ! -e $old_path ]]; then + git -C "$INNER_GIT_DIR" rm -qr "$old_path" 2>/dev/null + set_git "$new_path" git_add_file "$new_path" "Rename ${1} to ${2}." fi + set_git "$old_path" + if [[ -n $INNER_GIT_DIR && ! -e $old_path ]]; then + git -C "$INNER_GIT_DIR" rm -qr "$old_path" 2>/dev/null + set_git "$old_path" + [[ -n $(git -C "$INNER_GIT_DIR" status --porcelain "$old_path") ]] && git_commit "Remove ${1}." + fi rmdir -p "$old_dir" 2>/dev/null else cp $interactive -r -v "$old_path" "$new_path" || exit 1 @@ -605,18 +627,20 @@ cmd_copy_move() { } cmd_git() { + set_git "$PREFIX/" if [[ $1 == "init" ]]; then - git "$@" || exit 1 + INNER_GIT_DIR="$PREFIX" + git -C "$INNER_GIT_DIR" "$@" || exit 1 git_add_file "$PREFIX" "Add current contents of password store." echo '*.gpg diff=gpg' > "$PREFIX/.gitattributes" git_add_file .gitattributes "Configure git repository for gpg file diff." - git config --local diff.gpg.binary true - git config --local diff.gpg.textconv "$GPG -d ${GPG_OPTS[*]}" - elif [[ -e $GIT_DIR ]]; then + git -C "$INNER_GIT_DIR" config --local diff.gpg.binary true + git -C "$INNER_GIT_DIR" config --local diff.gpg.textconv "$GPG -d ${GPG_OPTS[*]}" + elif [[ -n $INNER_GIT_DIR ]]; then tmpdir nowarn #Defines $SECURE_TMPDIR. We don't warn, because at most, this only copies encrypted files. export TMPDIR="$SECURE_TMPDIR" - git "$@" + git -C "$INNER_GIT_DIR" "$@" else die "Error: the password store is not a git repository. Try \"$PROGRAM git init\"." fi -- cgit v1.2.3-59-g8ed1b