gopass History
Version history navigation and secret recovery.
Git-Backed Password Store
gopass stores everything in git. Every change creates a commit with: - What changed (the encrypted .gpg file) - When it changed (commit timestamp) - Who changed it (GPG signature if enabled)
BENEFIT: Full audit trail, full rollback, full disaster recovery.
View History
# Store commit history (default store)
gopass git log
# Specific store history
gopass git log --store v3
# Last 10 commits
gopass git log --oneline -10
# What changed in a commit
gopass git show <commit-hash>
# Full git log (in store directory)
cd ~/.local/share/gopass/stores/v3
git log --oneline
git log --oneline --graph
# Changes to specific entry
git log --oneline -- path/to/entry.gpg
# Who changed what
git log --format="%h %an %s" -10
# Changes in time range
git log --since="2026-02-01" --until="2026-02-28" --oneline
Find Deleted Entries
# List ALL deleted entries in store
cd ~/.local/share/gopass/stores/v3
git log --diff-filter=D --summary --oneline
# Extract just the deleted paths
git log --diff-filter=D --summary --oneline | grep 'delete mode' | awk '{print $NF}'
# Find when specific entry was deleted
git log --all --full-history -- path/to/entry.gpg
# Find entries deleted in last 30 days
git log --diff-filter=D --summary --oneline --since="30 days ago"
# Search for deleted entry by keyword
git log --all --full-history --diff-filter=D --name-only --oneline | grep -i keyword
Recover Deleted Entry
# Step 1: Find the deletion commit
cd ~/.local/share/gopass/stores/v3
git log --all --full-history -- path/to/entry.gpg
# Note: Find commit BEFORE the deletion (the ^ means parent)
# Step 2: Restore the entry
# The ^ after commit hash means "parent of this commit" (state before deletion)
git checkout <deletion-commit-hash>^ -- path/to/entry.gpg
# Step 3: Verify recovery
gopass show path/to/entry
# Step 4: Commit the restoration
git add path/to/entry.gpg
git commit -m "Restore deleted entry: path/to/entry"
# Alternative: Restore entire directory
git checkout <commit-hash>^ -- path/to/directory/
# Alternative: Restore to different path
git show <commit-hash>^:path/to/entry.gpg > new/path/entry.gpg
git add new/path/entry.gpg
git commit -m "Restore entry to new location"
TIP: The caret ^ is crucial. Without it, you get the state AFTER deletion (empty).
Revert Changes
# Undo last commit (creates new revert commit)
gopass git revert HEAD
# Undo last commit in specific store
gopass git revert HEAD --store v3
# Revert specific commit
gopass git revert <commit-hash>
# Revert multiple commits (interactive)
cd ~/.local/share/gopass/stores/v3
git revert <oldest-hash>^..<newest-hash>
# Hard reset to previous state (DANGEROUS - rewrites history)
# Only use if NOT pushed to remote yet
cd ~/.local/share/gopass/stores/v3
git reset --hard HEAD~1
# Reset to specific commit
git reset --hard <commit-hash>
REVERT vs RESET:
- revert: Creates new commit that undoes changes (safe, preserves history)
- reset --hard: Rewrites history, loses commits (DANGEROUS)
Compare Versions
# See what changed between commits
cd ~/.local/share/gopass/stores/v3
git diff <old-hash> <new-hash>
# Changes in specific entry
git diff <old-hash> <new-hash> -- path/to/entry.gpg
# Note: Shows encrypted binary diff (not useful)
# To actually compare content, decrypt both versions
git show <old-hash>:path/to/entry.gpg | gpg -d > /tmp/old.txt
git show <new-hash>:path/to/entry.gpg | gpg -d > /tmp/new.txt
diff /tmp/old.txt /tmp/new.txt
rm /tmp/old.txt /tmp/new.txt
# Show entry at specific point in time
git show <commit-hash>:path/to/entry.gpg | gpg -d
Audit Trail
# When was entry last modified
cd ~/.local/share/gopass/stores/v3
git log -1 --format="%ai %s" -- path/to/entry.gpg
# All modifications to an entry
git log --format="%ai %h %s" -- path/to/entry.gpg
# Who modified entry
git log --format="%an <%ae>" -- path/to/entry.gpg | sort -u
# Changes by specific author
git log --author="username" --oneline
# Generate audit report for path
echo "=== Audit Report for path/to/entry ==="
echo "Created: $(git log --reverse --format='%ai' -- path/to/entry.gpg | head -1)"
echo "Last Modified: $(git log -1 --format='%ai' -- path/to/entry.gpg)"
echo "Total Changes: $(git log --oneline -- path/to/entry.gpg | wc -l)"
echo "Modified By:"
git log --format=' - %an (%ai)' -- path/to/entry.gpg
# Export full audit for compliance
git log --format='%ai|%an|%h|%s' > audit-$(date +%Y%m%d).csv
Safe Migration Pattern
# Before any migration: create bookmark
cd ~/.local/share/gopass/stores/v3
git log --oneline -1 # Note this commit hash
# Migration: Copy to new location
gopass show old/path/entry | gopass insert -m new/path/entry
# Verify new entry works
gopass show new/path/entry
# Remove old entry
gopass rm old/path/entry
# Verify migration
gopass ls new/path/
gopass show new/path/entry
# If something went wrong, restore from bookmark
git checkout <bookmark-hash> -- old/path/entry.gpg
git add old/path/entry.gpg
git commit -m "Restore entry after failed migration"
# Bulk migration with verification
for entry in $(gopass ls -f old-store/path/); do
new_entry=$(echo "$entry" | sed 's|old-store/|new-store/|')
# Copy
gopass show "$entry" | gopass insert -m "$new_entry"
# Verify (compare passwords)
old_pass=$(gopass show -o "$entry")
new_pass=$(gopass show -o "$new_entry")
if [[ "$old_pass" == "$new_pass" ]]; then
echo "✓ Migrated: $entry"
else
echo "✗ FAILED: $entry"
fi
done
Branch Operations
# Create branch for experimental changes
cd ~/.local/share/gopass/stores/v3
git checkout -b reorganization
# Make changes
gopass mv old/structure new/structure
gopass rm legacy/entries
# If happy, merge to main
git checkout main
git merge reorganization
# If not happy, abandon branch
git checkout main
git branch -D reorganization
# Compare branches
git log main..reorganization --oneline # What's in branch but not main
git diff main reorganization # Actual differences
# Cherry-pick specific commit
git cherry-pick <commit-hash>
USE CASE: Test store reorganization before committing to it.
Handling Sync Conflicts
# When gopass sync fails due to conflict
# Step 1: Check status
cd ~/.local/share/gopass/stores/v3
git status
# Step 2: Pull with rebase (usually works)
git pull --rebase origin main
# Step 3: If conflicts, resolve manually
git status # Shows conflicted files
# For encrypted files, you usually want one or the other:
git checkout --ours path/to/entry.gpg # Keep local version
git checkout --theirs path/to/entry.gpg # Keep remote version
# Step 4: Complete the rebase
git add .
git rebase --continue
# Step 5: Push
git push origin main
# Nuclear option: Force local to match remote
git fetch origin
git reset --hard origin/main
# Or force remote to match local (DANGEROUS)
git push --force origin main
Git Housekeeping
# Check store health
gopass fsck
# Git garbage collection
cd ~/.local/share/gopass/stores/v3
git gc
# Aggressive cleanup (after large migrations)
git gc --aggressive --prune=now
# Check repository size
du -sh .git
# Find large objects (unusual for gopass)
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print $3, $4}' | sort -rn | head -10
# Verify repository integrity
git fsck --full
Disaster Recovery
# Primary recovery: Clone from remote
rm -rf ~/.local/share/gopass/stores/v3 # CAREFUL
git clone git@github.com:user/gopass-v3.git ~/.local/share/gopass/stores/v3
gopass mounts add v3 ~/.local/share/gopass/stores/v3
# Recovery from local backup
tar xzf gopass-backup.tar.gz -C ~/.local/share/gopass/
gopass mounts add v3 ~/.local/share/gopass/stores/v3
# Recovery from reflog (if commits were lost locally)
cd ~/.local/share/gopass/stores/v3
git reflog
# Find the commit before disaster
git checkout <reflog-hash>
git checkout -b recovery
git checkout main
git reset --hard recovery
# Export all entries to plaintext (for migration)
for entry in $(gopass ls -f v3/); do
mkdir -p "export/$(dirname "$entry")"
gopass show "$entry" > "export/$entry.txt"
done
# WARNING: This creates unencrypted files!
# Re-import after setup
for file in $(find export -name "*.txt"); do
entry=$(echo "$file" | sed 's|export/||; s|\.txt$||')
cat "$file" | gopass insert -m "$entry"
done
rm -rf export
CRITICAL REQUIREMENTS:
1. GPG private key (without this, nothing can be decrypted)
2. Git remote URL (for clone recovery)
3. Mount configuration (recreate with gopass mounts add)
Common Gotchas
# GOTCHA: Forgetting ^ when restoring
git checkout <commit>^ -- file # CORRECT - before deletion
git checkout <commit> -- file # WRONG - after deletion (empty)
# GOTCHA: reset --hard loses data
# ALWAYS use revert for shared branches
gopass git revert HEAD # Safe, creates revert commit
# GOTCHA: Encrypted files can't be diffed meaningfully
# Git shows binary diff - decrypt to compare
# GOTCHA: GPG signatures may prevent push
# If GPG signing fails, commits can't be made
gpg --list-secret-keys # Verify key exists
gpgconf --kill gpg-agent # Restart agent
# GOTCHA: Reflog expires
# Old commits eventually get garbage collected
git gc --prune=now # Forces immediate cleanup
# Recovery only works if reflog entries exist