>_ZSH-GIT-ACP
Ctrl-S — stage all, commit with the current buffer as message, push. Two ZLE widgets take the current $BUFFER as the commit message and run git pull --rebase, git add ., git commit -m "$BUFFER", git push. When the repo has no remote, the pull and push steps are skipped. Plus 159 git aliases declared in zsh-git-acp.plugin.zsh. Pure zsh, no compiled state, no background process.
Overview
This plugin compresses the inner loop git pull && git add . && git commit -m "..." && git push into a single keystroke. It registers two ZLE widgets, binds them across the viins, vicmd and emacs keymaps, and ships a curated set of 159 git aliases. Six autoload helper functions back the widgets and the branch-aware aliases.
| Source size | 510 zsh lines — 268-line plugin entry + 6 autoload helpers |
| Aliases | 159 git aliases (line 12–218 of the plugin) |
| ZLE widgets | zsh-gacp-NoCheck, zsh-gacp-CheckDiff |
| Key bindings | ^S and ^F^S in viins + vicmd + emacs |
| Required tools | git; less and a diff tool for the ^F^S review path |
| Shell side effect | setopt noflowcontrol at source time (frees ^S/^Q) |
Install
The plugin is a single sourced file. Pick whichever loader matches your setup.
# Zinit
source "$HOME/.zinit/bin/zinit.zsh"
zinit ice lucid nocompile
zinit load MenkeTechnologies/zsh-git-acp
# Oh My Zsh
cd "$HOME/.oh-my-zsh/custom/plugins" \
&& git clone https://github.com/MenkeTechnologies/zsh-git-acp.git
# then add zsh-git-acp to the plugins=( ... ) array in ~/.zshrc
# Manual
git clone https://github.com/MenkeTechnologies/zsh-git-acp
source zsh-git-acp/zsh-git-acp.plugin.zsh
On load the plugin runs the Zsh Plugin Standard 0= header to resolve its own path, prepends ${0:h}/autoload to fpath, autoloads the six helper functions with autoload -Uz "${0:h}/autoload/"*(.:t), defines the 159 aliases, registers the two widgets with zle -N, runs setopt noflowcontrol, and binds the keys. Sourcing is idempotent — re-sourcing does not duplicate aliases or widgets.
Key bindings
^S (viins + vicmd + emacs) | zsh-gacp-NoCheck — pull / add / commit / push with no confirmation. The plugin runs setopt noflowcontrol so ^S reaches ZLE instead of being eaten as terminal XOFF. |
^F^S (viins + vicmd + emacs) | zsh-gacp-CheckDiff — stages, then shows the diff piped into less -R and prompts for a single-key confirmation before committing and pushing. |
Both widgets take the pre-press $BUFFER as the literal commit message (git commit -m "$BUFFER") — no editor pop-up. On success the buffer is pushed to history with print -sr and the line is cleared with zle .kill-whole-line. The exact bindkey lines from the plugin:
bindkey -M viins '^S' zsh-gacp-NoCheck bindkey -M vicmd '^S' zsh-gacp-NoCheck bindkey -M emacs '^S' zsh-gacp-NoCheck bindkey -M viins '^F^S' zsh-gacp-CheckDiff bindkey -M vicmd '^F^S' zsh-gacp-CheckDiff bindkey -M emacs '^F^S' zsh-gacp-CheckDiff
The two widgets
zsh-gacp-NoCheck | Validates the directory against the blacklist, that the cwd is a git dir (zpwrIsGitDir), that $BUFFER is non-empty, and that git status does not report nothing to commit. Then it calls zsh-gacp-CommitAndPush "$BUFFER" — no diff, no prompt. |
zsh-gacp-CheckDiff | Same guards, then runs git add . and shows a diff for review: it uses gitSdiffColorizer.pl | less -R if that script is on $PATH, otherwise git difftool -y -x HEAD * | less -R. It then prints a >>>>>> Push? prompt and reads one key; on y it calls zsh-gacp-CommitAndPush "$BUFFER". |
zsh-gacp-CommitAndPush | The shared backend. Rejects an empty message, checks git-dir, checks the blacklist, and bails on nothing to commit. If any remote exists it runs git pull --rebase, then always git add . + git commit -m "$1", then git push if a remote exists. The push is a plain git push — no --force. |
Configuration — directory blacklist
To stop the widgets from auto-committing in particular directories, set an array of absolute paths. A widget compares $(pwd -P) against each entry and aborts with a red error if matched:
export ZSH_GACP_BLACKLISTED_DIRECTORIES=(/etc "$HOME/.ssh" /var/log/repo)
Note the variable name differs by widget in the current source: zsh-gacp-CheckDiff and zsh-gacp-CommitAndPush read $ZSH_GACP_BLACKLISTED_DIRECTORIES (the documented name), while zsh-gacp-NoCheck reads the un-prefixed $GACP_BLACKLISTED_DIRECTORIES. This divergence is pinned by a test (t-nocheck-blacklist.zsh). To blacklist a directory for every path, export both names.
The plugin also sets two defaults at load: ZPWR_DEV_BRANCH="devBranch" and ZPWR_MAIN_BRANCH="master".
Branch detection
Many aliases call helper functions at expansion time so the same alias works whether a repo uses master or main:
zsh-gacp-mainBranch [remote] | Probes refs/remotes/$remote/$branch for master then main; echoes the first that exists, else master. Default remote is origin; returns 1 outside a git dir. |
zsh-gacp-devBranch [remote] | Probes development, develop, devel in that order; echoes the first that exists, else dev. Default remote is origin. |
zsh-gacp-CommitAndPush (file) | Shared commit/push backend (see above). |
zsh-gacp-CommitCount (file) | Returns git rev-list HEAD | wc -l, the commit count of the current branch. Standalone helper, not wired into the widgets. |
Aliases (sample)
The 159 aliases cover the branch / checkout / diff / fetch / log / merge / pull / push / remote / reset / tag families, each with origin- and upstream-aware variants. A representative slice:
gacp | git add . && git commit -m '' && git push |
gac | git add . && git commit -m '' |
gbuom / gbuod | git branch -u origin/$(zsh-gacp-mainBranch) / dev |
gcm / gcd | checkout main / dev branch |
gdom / gdod | git diff -w against origin main / dev |
gmom / gmod | git merge origin/ main / dev |
glr / gj | git pull --rebase / git pull --rebase --autostash -v |
gpf / gpfsup | git push --force / force push + set upstream to current HEAD |
glgf | git log --stat --format=fuller |
gsa | git rev-list --all | xargs git grep -C 5 |
bk / bki | git clean -dff && git reset --hard HEAD && git clean -dff (-x variant) |
allRebase | git rebase -i $(git rev-list --max-parents=0 HEAD) |
The full 159-alias list lives in the README "159 git aliases" section. For the per-file source breakdown and test coverage, see the Engineering Report.
Troubleshooting
^S freezes the terminal | Flow control is still on. The plugin runs setopt noflowcontrol; confirm the plugin actually sourced and that nothing later re-enables flow control with setopt flowcontrol. |
| “is not a git dir” | The widget ran outside a git working tree. git rev-parse --git-dir must succeed in the cwd. |
| “No commit message” | The command-line buffer was empty (only whitespace). Type the message first, then press ^S. |
| “Nothing to commit” | git status reports a clean tree after git add . — there is nothing to record. |
| BLACKLISTED message | The cwd matches an entry in the blacklist array. Remove the path or run from elsewhere. |
| No push happened | The repo has no configured remote; git remote is empty, so pull and push are skipped by design. |
Sibling plugins
Part of the MenkeTechnologies zsh plugin family — the MenkeTechnologiesMeta umbrella:
- zsh-better-npm-completion — cache-aware
npm installcompletion - zsh-cargo-completion — live
cargo add/cargo installcompletion - zsh-cpan-completion — live MetaCPAN
cpan/cpanmcompletion - zsh-dotnet-completion — dotnet CLI completion
- zsh-expand — spacebar expansion (11,683 tests)
- zsh-gem-completion — ruby
gem installremote completion - zsh-git-repo-cache — index every git repo on disk
- zsh-learn — MySQL-backed learning collection / quiz
- zsh-more-completions — 33,175-file mega completion corpus
- zsh-nginx — nginx commands + service-wrapper aliases
- zsh-pip-description-completion — pip remote completion with descriptions
- zsh-sed-sub — in-place sed substitution on the command line
- zsh-sudo — ESC ESC to prepend sudo
- zsh-xcode-completions — xcodebuild / xcrun / swift completion