`git push origin --delete <TAB>` completes to `origin/foo`, but `origin/foo` is a LOCAL remote-tracking name — the ref on the server is just `foo`. So the command zsh completes for you is the one git rejects, and it rejects it with "remote ref does not exist", which reads as "that branch is gone" rather than "you named it wrong". The second half is worse, because it makes the first half look intermittent: without pruning, `git branch -r` and the completion keep offering branches that were deleted on the server weeks ago. Then some completions fail because the branch is genuinely gone and others fail because of the prefix, with identical error text. Two fixes, one per cause: - `fetch.prune = true` globally, so stale remote-tracking refs are dropped on every fetch/pull and dead branches stop being offered at all. - `grmb`, which takes either form, prunes first, and reports per-branch rather than aborting the batch. `grmb origin/foo` and `grmb foo` both work. Named grmb rather than the more obvious gbd because oh-my-zsh's git plugin already binds gbd to `git branch --delete`. Aliases resolve before functions, so the collision would not have errored — it would have silently tried to delete a LOCAL branch when the whole point is the remote one. Caught by `type gbd` after installing; check any new g* helper the same way. grmb deliberately does not wrap `git push` itself — intercepting a git subcommand to rewrite its arguments is the kind of thing that works until it silently does the wrong thing to a refspec someone meant literally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
81 lines
3.0 KiB
Bash
81 lines
3.0 KiB
Bash
# Managed by dotfiles/zsh/install.sh — edits made directly to ~/.zshrc will be
|
|
# overwritten on the next bootstrap run. Edit zsh/zshrc in the dotfiles repo
|
|
# instead.
|
|
|
|
# If this account was added to the docker group (dotfiles/docker/install.sh)
|
|
# but this shell's session predates that, re-exec via newgrp so docker works
|
|
# without a full logout. Guarded: fires only when the group is on the account
|
|
# yet missing from the running shell's groups, so the newgrp'd shell (which
|
|
# has the group) cannot loop.
|
|
# Only for real terminals ([ -t 0 ]): newgrp replaces the shell and would
|
|
# swallow the command in scripted `zsh -i -c ...` invocations.
|
|
if [ -t 0 ] && [ -S /var/run/docker.sock ] \
|
|
&& id -nG "${USER:-$(id -un)}" 2>/dev/null | grep -qw docker \
|
|
&& ! id -Gn 2>/dev/null | grep -qw docker; then
|
|
exec newgrp docker
|
|
fi
|
|
|
|
# Userland tools installed by dotfiles/bin/install.sh live here.
|
|
case ":$PATH:" in
|
|
*":$HOME/.local/bin:"*) ;;
|
|
*) export PATH="$HOME/.local/bin:$PATH" ;;
|
|
esac
|
|
|
|
export ZSH="$HOME/.oh-my-zsh"
|
|
|
|
ZSH_THEME="robbyrussell"
|
|
|
|
plugins=(git)
|
|
|
|
source "$ZSH/oh-my-zsh.sh"
|
|
|
|
HISTSIZE=10000
|
|
SAVEHIST=10000
|
|
setopt SHARE_HISTORY
|
|
setopt HIST_IGNORE_DUPS
|
|
|
|
# Delete remote branches without fighting tab-completion.
|
|
#
|
|
# `git push origin --delete <TAB>` completes to `origin/foo`, but `origin/foo`
|
|
# is a LOCAL remote-tracking name — the ref on the server is just `foo`. So the
|
|
# command zsh completes for you is the one git rejects, with "remote ref does
|
|
# not exist", which reads like the branch is missing rather than misnamed.
|
|
#
|
|
# grmb takes either form. It fetches with --prune first so branches already
|
|
# deleted on the server stop being offered at all (that is the other half of
|
|
# this annoyance, and `fetch.prune = true` from dotfiles/git/install.sh makes it
|
|
# automatic everywhere else).
|
|
#
|
|
# Named grmb, not gbd: oh-my-zsh's git plugin already binds gbd to
|
|
# `git branch --delete`, aliases resolve before functions, and the collision
|
|
# fails in the worst possible way — `gbd origin/foo` would quietly try to
|
|
# delete a LOCAL branch instead of the remote one. Check with `type <name>`
|
|
# before adding any g* helper here.
|
|
#
|
|
# grmb origin/my-branch # completed form, works
|
|
# grmb my-branch other # bare form, several at once
|
|
grmb() {
|
|
emulate -L zsh
|
|
local remote=origin b rc=0
|
|
if [ "$#" -eq 0 ]; then
|
|
print -u2 "grmb: usage: grmb <branch>... (origin/ prefix optional)"
|
|
return 2
|
|
fi
|
|
git fetch --prune --quiet "$remote" || return 1
|
|
for b in "$@"; do
|
|
b=${b#${remote}/}
|
|
if git push "$remote" --delete "$b"; then
|
|
print "deleted ${remote}/${b}"
|
|
else
|
|
print -u2 "grmb: could not delete ${b} (already gone? check: git ls-remote --heads ${remote})"
|
|
rc=1
|
|
fi
|
|
done
|
|
return $rc
|
|
}
|
|
|
|
# Per-machine customizations (secrets, keys, local overrides) live in
|
|
# ~/.zshrc.local, which bootstrap never touches. See zsh/zshrc.local.example
|
|
# for the variables modules look for. Loaded last so it can override anything.
|
|
[ -f "$HOME/.zshrc.local" ] && source "$HOME/.zshrc.local"
|