Skip to content

Instantly share code, notes, and snippets.

@eskil
Last active December 16, 2025 17:11
Show Gist options
  • Select an option

  • Save eskil/2629551a9d02f1ea9427e23a0cceb83d to your computer and use it in GitHub Desktop.

Select an option

Save eskil/2629551a9d02f1ea9427e23a0cceb83d to your computer and use it in GitHub Desktop.
Some git aliases I use
#!/usr/bin/env bash
set -euo pipefail
echo "Pruning..."
git fetch --prune --quiet
# Collect branches whose upstream is gone
branches=$(git for-each-ref \
--format='%(refname:short) %(upstream:track)' refs/heads \
| awk '$2 == "[gone]" {print $1}')
if [[ -z "$branches" ]]; then
echo "No gone branches."
exit 0
fi
# fzf selection with preview: last commit
selected=$(echo "$branches" | fzf \
--multi \
--prompt="Select gone branches to delete: " \
--ansi \
--reverse \
--marker="x " \
--pointer="- " \
--preview='git log -n 3 --color=always --decorate --oneline --graph --pretty=medium {1}' \
--color='pointer:161,marker:168' \
--preview-window=right:50%)
if [[ -z "$selected" ]]; then
echo "No branches selected."
exit 0
fi
current_branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
needs_checkout=false
# If current branch is among the branches to delete → auto-checkout safe branch
if echo "$selected" | grep -qx "$current_branch"; then
needs_checkout=true
fi
if $needs_checkout; then
echo "Current branch '$current_branch' is selected for deletion."
echo "Checking out a safe branch..."
safe=""
# Priority 1: main
if git rev-parse --verify main >/dev/null 2>&1; then
safe="main"
# Priority 2: master
elif git rev-parse --verify master >/dev/null 2>&1; then
safe="master"
# Priority 3: upstream default branch (if available)
elif upstream_default=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null); then
safe="${upstream_default#refs/remotes/origin/}"
fi
if [[ -n "$safe" ]]; then
git checkout "$safe"
else
echo "No safe branch found. Detaching HEAD."
git checkout --detach
fi
fi
# Proceed with deletions
echo "$selected" | while read -r br; do
echo "Deleting branch: $br"
git branch -D "$br"
done
#!/usr/bin/env bash
set -euo pipefail
# Usage: git recent [compare_branch] [count]
refbranch="${1:-origin/master}"
count="${2:-20}"
# Collect branch metadata
rawlist=$(
git for-each-ref \
--sort=-committerdate \
--format='%(refname:short)|%(HEAD)%(color:yellow)%(refname:short)|%(color:bold green)%(committerdate:relative)|%(color:blue)%(subject)|%(color:magenta)%(authorname)%(color:reset)' \
--color=always \
--count="$count" \
refs/heads \
| while IFS='|' read -r branch colored_branch date msg author; do
# Remove possible leading '*' from current branch indicator
clean_branch=$(echo "$branch" | tr -d '*')
# Ahead/behind counts
ahead=$( git rev-list --count "$refbranch..$clean_branch" 2>/dev/null || echo 0 )
behind=$( git rev-list --count "$clean_branch..$refbranch" 2>/dev/null || echo 0 )
# Output format for further processing
printf "%s|%s|%s|%s|%.70s|%s\n" \
"$ahead" "$behind" "$colored_branch" "$date" "$msg" "$author"
done
)
# Nicely aligned table for fzf display
displaylist=$(printf "%s\n" "$rawlist" | column -ts '|')
# Launch fzf
selected=$(printf "%b" "$displaylist" | fzf \
--ansi \
--with-nth=1.. \
--delimiter='|' \
--prompt='Recent Branches › ' \
--reverse \
--color='pointer:161,marker:168'
)
# If user cancelled
if [[ -z "$selected" ]]; then
exit 0
fi
# Extract the branch name (3rd column)
branch=$(echo "$selected" | awk '{print $3}')
echo "Switching to $branch"
git switch "$branch"
#!/usr/bin/env bash
set -euo pipefail
# ------------------------------
# CONFIGURATION
# ------------------------------
PROTECTED_PATTERNS=(
"*.env"
"*.pem"
"*.key"
"*.crt"
"*.p12"
"*secrets*"
)
EDITOR="${EDITOR:-vim}"
# ------------------------------
# HELPERS
# ------------------------------
is_protected() {
local f="$1"
for pat in "${PROTECTED_PATTERNS[@]}"; do
[[ "$f" == $pat ]] && return 0
done
return 1
}
# Portable move-to-trash:
move_to_trash() {
local f="$1"
# Try trash-put (trash-cli)
if command -v trash-put >/dev/null 2>&1; then
trash-put "$f" && return 0
fi
# Try gio (GNOME)
if command -v gio >/dev/null 2>&1; then
gio trash "$f" && return 0
fi
# Try gvfs-trash (older systems)
if command -v gvfs-trash >/dev/null 2>&1; then
gvfs-trash "$f" && return 0
fi
# macOS: use AppleScript to move to Trash
# macOS Finder
if [[ "$(uname -s)" == "Darwin" ]]; then
for f in "$@"; do
# Resolve absolute path
if command -v realpath >/dev/null 2>&1; then
absf=$(realpath "$f")
else
absf="$PWD/$f"
fi
/usr/bin/osascript -e "tell application \"Finder\" to move (POSIX file \"$absf\") to trash"
done
return
fi
# # Fallback: move into $XDG_DATA_HOME/Trash/files or ~/.local/share/Trash/files
# echo "\t try fallback"
# local trash_dir="${XDG_DATA_HOME:-$HOME/.local/share}/Trash/files"
# mkdir -p "$trash_dir"
# # If file name collision, append timestamp
# local base
# base=$(basename -- "$f")
# if [[ -e "$trash_dir/$base" ]]; then
# mv -- "$f" "$trash_dir/${base}.$(date +%s)" && return 0
# else
# mv -- "$f" "$trash_dir/" && return 0
# fi
return 1
}
# Permanent delete
permanent_delete() {
local f="$1"
rm -f -- "$f"
}
# ------------------------------
# BUILD A TEMP PREVIEW SCRIPT (robust)
# ------------------------------
PREVIEW_SCRIPT=$(mktemp)
chmod +x "$PREVIEW_SCRIPT"
cat > "$PREVIEW_SCRIPT" <<'PREVIEW_EOF'
#!/usr/bin/env bash
# arg1 -> path to file to preview
f="$1"
# If not a regular file, show message
if [[ ! -f "$f" ]]; then
echo "Not a regular file"
exit 0
fi
# Protected check: we can't access PROTECTED_PATTERNS array from parent here, so
# replicate a small subset of patterns or read them from env var if exported.
# We'll accept PROTECTED list in PROTECT_PATTERNS env if present.
if [[ -n "${PROTECT_PATTERNS:-}" ]]; then
IFS=$'\n' read -r -d '' -a pats <<< "$PROTECT_PATTERNS" || true
for pat in "${pats[@]}"; do
if [[ "$f" == $pat ]]; then
echo "[ PROTECTED FILE ]"
echo "Matches protected pattern: $pat"
echo
break
fi
done
fi
# Show text preview if file command says text
if file "$f" | grep -qi text; then
if command -v bat >/dev/null 2>&1; then
bat --style=plain --color=always --line-range=1:200 "$f"
else
head -n 200 "$f"
fi
else
echo "[binary file]"
fi
PREVIEW_EOF
# Ensure temp preview script is removed on exit
cleanup() {
rm -f "$PREVIEW_SCRIPT"
}
trap cleanup EXIT
# Export protected patterns into a single-line env var for preview script
# Use null-separated to be safe
PROTECT_PATTERNS=$(printf "%s\n" "${PROTECTED_PATTERNS[@]}")
export PROTECT_PATTERNS
# ------------------------------
# MAIN
# ------------------------------
files=$(git ls-files --others --exclude-standard)
if [[ -z "$files" ]]; then
echo "No untracked files."
exit 0
fi
# keep original fzf options the user requested
selected=$(printf "%s\n" "$files" | fzf \
--multi \
--prompt="Untracked Files › " \
--border \
--ansi \
--reverse \
--marker="x " \
--pointer="- " \
--preview="$PREVIEW_SCRIPT {}" \
--color='pointer:161,marker:168' \
--preview-window=right:50% \
--bind="ctrl-o:execute($EDITOR {+})" \
--bind="ctrl-e:execute($EDITOR {+})"
# --bind="ctrl-d:execute(
# echo 'Force deleting:' {+};
# for f in {+}; do rm -f \"\$f\"; done;
# echo 'Done';
# sleep 1
# )+abort"
)
# If user aborted / nothing selected
if [[ -z "${selected:-}" ]]; then
exit 0
fi
# ------------------------------
# NORMAL DELETE (Enter key): move to trash unless protected
# ------------------------------
# iterate selected lines (could be multiple)
printf "%s\n" "$selected" | while IFS= read -r f; do
if is_protected "$f"; then
echo "Skipping PROTECTED file: $f"
continue
fi
echo "Moving to trash: $f"
if move_to_trash "$f"; then
echo " → Moved to trash"
else
echo " → Failed to move to trash, attempting permanent delete"
permanent_delete "$f"
fi
done
[alias]
recent = "!git-recent.sh"
gone = "!git-gone.sh"
what = "!git-what.sh"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment