Zsh: set aside a command with “Escape Q” / push-line

Do you ever do this? You carefully type out a command, like this pytest one:
$ pytest tests/test_checkout.py -k discount --reuse-db -x
…then, just before pressing Enter, you remember that something will stop it from succeeding, like being on the wrong Git branch.
What now?
Maybe you run the command anyway, to put it in your history, letting it fail or cancelling it with Ctrl-C. Then you can run your setup command.
Or maybe you press Ctrl-A to jump to the start, and add the setup command first, like git switch fix-discounts &&, and press Enter. Then both commands will run together, but they’re in one combined history entry.
Both of these options work, but they’re fiddly.
Zsh has a neater feature to help here: the push-line widget. Press Escape, release it, then press q. Zsh stores your current command away and clears the prompt, ready for a different command:
$ pytest tests/test_checkout.py -k discount --reuse-db -x<Escape q>
$
Now you can run whatever you need, such as switching branch:
$ git switch fix-discounts
Switched to branch 'fix-discounts'
When that command finishes, Zsh restores the stored command at the next prompt, with the cursor at the end, ready to run:
$ pytest tests/test_checkout.py -k discount --reuse-db -x
Press Enter, and your tests run on the right branch.
push-line works for any “oops, I forgot to do a thing first” moment, like:
- Activating a virtual environment.
- Changing to a different directory.
- Checking a file name with
ls. - Pulling the latest changes with
git pull.
Use push-line-or-edit instead
I recommend rebinding Esc q to a variant widget, push-line-or-edit, in your ~/.zshrc:
# Make Escape Q push the current line, or the whole edit if it’s multi-line.
# Per: https://adamj.eu/tech/2026/10/06/zsh-push-line/
bindkey '^[q' push-line-or-edit
At a normal prompt, push-line-or-edit behaves exactly like push-line. The difference comes with multi-line commands (“edits”), such as a long pytest command split with backslash line continuations:
$ pytest tests/test_checkout.py \
∙ -k discount \
∙ --reuse-db \
∙ -x
Here, the default push-line widget only stores the current line, -x, which is not much use. push-line-or-edit instead pulls the whole command back into a single editable buffer:
$ pytest tests/test_checkout.py \
-k discount \
--reuse-db \
-x
Press Escape, q again, and the whole command is set aside, ready to return after your next command. So push-line-or-edit kinda just does the right thing, with no real downside. I am not sure why it’s not the default.
Oh My Zsh
If you use Oh My Zsh, like me, you get a few extras around push-line:
- It selects the Emacs keymap, where
push-lineis bound to Escape q, Escape Q, and Control-Q. - It disables terminal flow control, so Control-Q reaches Zsh rather than being swallowed by the terminal. Without Oh My Zsh, Control-Q may do nothing.
- It binds Escape l to push the current line, run
ls, and then restore the line. That’s handy when you need to check a file name mid-command.
But Oh My Zsh doesn’t bind push-line-or-edit, so you’ll still want to add the rebinding above. To cover all three shortcuts:
# Make Escape q, Escape q, and Control-Q push the current line, or the whole
# edit if it’s multi-line.
# Per: https://adamj.eu/tech/2026/10/06/zsh-push-line/
bindkey '^[q' push-line-or-edit
bindkey '^[Q' push-line-or-edit
bindkey '^Q' push-line-or-edit
Check out my new book Boost Your GitHub DX.
One summary email a week, no spam, I pinky promise.
Related posts:
- Zsh: tab complete glob qualifiers
- Zsh: manipulate filenames with modifiers like
:r(root) and:e(extension) - Zsh: clear the “You have mail“ message
Tags: zsh