Skip to content

Custom keybindings

This page lists every rebindable action in gitoui, global UserEvents first, then each per-view scope, with the default keys, what each action does, and how to override them.

Two layers : the global map covers everything ([keybind] in TOML), scopes override the global map inside a specific view ([keybind.scope.<path>]).

When you press a key in a given view, gitoui walks this chain:

  1. Leaf scope, the deepest scope the view exposes (e.g. pr.conversation, rebase.resume). If the key is bound here, fire the action.
  2. Parent scopes, walk up one level at a time. From pr.conversationpr. From rebase.resumerebase.
  3. Global, the [keybind] section.
  4. Literal forward, neither matched → the view gets the raw key event for its own char-level matches (rare).

The key fires the first action it matches. Once a leaf binds a key, parents are skipped.

Inheritance, sub-views see their parent’s bindings

Section titled “Inheritance, sub-views see their parent’s bindings”

A sub-view automatically inherits every key its parent scope defines. You only need to list a binding in the leaf scope if you want it to override the parent.

For example, in the PR view:

  • r is reload (defined in [scope.pr]).
  • The Conversation tab inherits that, pressing r there still reloads the PR.
  • But r is also bindable inside [scope.pr.conversation]. If you set it there, the conversation-level binding wins only on that tab.
  • Once you leave the tab, r falls back to the PR-wide reload.

The same chain runs in: pr.listpr → global, issues.detailissues → global, rebase.resumerebase → global, etc.

By contrast, the same key across different scopes is fine, that’s the whole point of scopes:

[keybind]
stage = ["a"] # global: `a` stages a file in Uncommitted
[keybind.scope.pr]
approve = ["a"] # in PR: `a` approves, overrides the global `stage`
[keybind.scope.issues]
assignees_picker = ["a"] # in Issues: `a` opens the assignees picker

When you’re in the PR view, a fires approve (and the global stage is silent). The global stage only fires in views without a scoped binding on a.

Merge semantics, overrides REPLACE, not extend

Section titled “Merge semantics, overrides REPLACE, not extend”

If your config sets approve = ["ctrl-a"], the default a → approve binding is dropped, only Ctrl+A triggers approve. To keep both, list them both explicitly:

[keybind.scope.pr]
approve = ["a", "ctrl-a"] # both keys fire approve

Anything you don’t touch keeps its full default key list.

gitoui accepts at most one modifier prefix per key ctrl-X alt-X shift-X. Chained combos like ctrl-shift-a are rejected at config-load.

Each key is a string. Format: [modifier-]base. Examples: q, ctrl-s, shift-h, f5, enter, esc, space.

Base keyAccepted spellings
Lettersa, b, … z (case-insensitive, shift-a and A are the same event).
Digits09
Functionf1f12
Arrowsup, down, left, right
Whitespacespace, tab, backtab
Editingenter, esc, backspace, delete, insert
Page navhome, end, pageup, pagedown
PunctuationThe character itself: ], +, #, =, -
Modifiersctrl-, alt-, shift- (max one prefix).

Empty array unbinds a default key entirely:

[keybind]
go_to_parent = [] # no key fires GoToParent

Available from every view. Use the action name on the left, give it any list of keys.

ActionDefaultWhat it does
force_quitctrl-cHard-quit, bypass any confirmation.
quitqQuit from the list / close the active view.
help_toggle? f1Open / close the in-app Help page.
cancelescCancel current action, close overlays, back out.
close(unbound)Alternate “close view” hook for custom flows.
configpOpen the Configuration view.
pull_requestsshift-rOpen the PR list (needs GitHub auth).
issuesshift-iOpen the Issues list (needs GitHub auth).
ActionDefaultWhat it does
navigate_upk upMove cursor up.
navigate_downj downMove cursor down.
navigate_lefth leftMove cursor / focus left.
navigate_rightl rightMove cursor / focus right.
go_to_parentalt-j alt-downDrill into the parent commit / branch.
select_upshift-kExtend selection up.
select_downshift-jExtend selection down.
go_to_topgJump to the top of the list.
go_to_bottomshift-gJump to the bottom.
scroll_upctrl-yScroll one line up (cursor stays).
scroll_downctrl-eScroll one line down.
page_upctrl-b pageupPage up.
page_downctrl-f pagedownPage down.
half_page_upctrl-uHalf-page up.
half_page_downctrl-dHalf-page down.
select_top(unbound)Position cursor at the top of the viewport.
select_middleshift-mPosition cursor at the middle of the viewport.
select_bottomshift-lPosition cursor at the bottom of the viewport.
confirmenterDrill into the focused item.
go_to_nextnJump to the next search match.
go_to_previousshift-nJump to the previous search match.
ref_listtabToggle the refs panel from the list view.
ActionDefaultWhat it does
searchfOpen the search bar in the list / diff view.
ignore_case_togglesToggle case-insensitive search in active mode.
fuzzy_togglezToggle fuzzy search.
ActionDefaultWhat it does
refreshrReload the view’s state from disk / API.
mark_comparespaceMark an endpoint for the 2-commit comparison flow.
pushshift-pgit push (current branch).
pullshift-ugit pull, also doubles as unstage_all / pull_branch in their respective views.
short_copyshift-cCopy short hash / short ref.
full_copycCopy full hash / commit message / path.
ActionDefaultWhat it does
add_tagtAdd a tag on the focused commit.
create_branchshift-bCreate a new branch starting at the focused commit.
blamebOpen blame for the focused file.
checkoutoCheckout the focused commit / branch.
cherry_pickshift-oCherry-pick the focused commit.
dropdDrop the focused commit (interactive rebase).
mergemMerge the focused branch into current.
rebaseeOpen the interactive rebase plan editor.
resetshift-sgit reset to the focused commit (mixed by default).
squashctrl-sSquash the focused commit into its parent.
amend_commitctrl-mAmend HEAD’s commit message (HEAD only).
file_historyshift-hgit log --follow on the focused file.
abort_operationctrl-aAbort an in-progress rebase / merge / cherry-pick.
ActionDefaultWhat it does
rename_branchctrl-rRename the focused branch.
delete_branchshift-dDelete the focused branch.
push_branchshift-qPush the focused branch.
pull_branch(unbound)Pull the focused branch, aliased on shift-u via the pull action in the BranchDetail view.
set_upstreamctrl-tSet / change the upstream for the focused branch.
create_archiveshift-egit archive the focused branch / tag.
unselect_branchshift-tClear the branch-detail selection.
copy_branch_nameshift-vCopy the focused branch name.
delete_tagshift-fDelete the focused tag.
push_tagshift-wPush the focused tag.
copy_tag_nameshift-yCopy the focused tag name.
ActionDefaultWhat it does
stageaStage the focused file (or open the conflict editor if the file is unmerged).
stage_allshift-aStage every unstaged + untracked file.
unstageuUnstage the focused file.
unstage_all(unbound)Unstage every staged file, aliased on shift-u via pull in the Uncommitted view.
discardxDiscard the focused unstaged change (confirmation).
discard_allshift-xDiscard every unstaged + staged change (confirmation).
stashiStash the working tree.
commitwOpen the commit message editor.
clean_untrackedvClean (delete) the focused untracked file.
ActionDefaultWhat it does
apply_stashyApply the focused stash.
pop_stashctrl-pPop the focused stash.
drop_stashctrl-xDrop the focused stash.
create_branch_from_stashctrl-nCreate a new branch from the stash.
copy_stash_namectrl-iCopy the stash name.
copy_stash_hashctrl-oCopy the stash hash.
ActionDefaultWhat it does
cycle_file_next+Jump to the next file in the current diff.
cycle_file_prev-Jump to the previous file.

User-defined shell commands (configured under [core.user_command]) bind through synthetic actions named user_command_N, where N matches the commands_N entry. None ship with a default key, every binding is user-set.

[keybind]
user_command_1 = ["f5"]
user_command_2 = ["f6"]

Full setup, placeholders and execution types live on the User commands page.

Each scope below ships with its own default key map. Anything you don’t list there keeps its parent / global default, you only need to write the action you want to change.

Inherits from: [keybind] (global).

ActionDefaultWhat it does
select_head_commithScroll to and center the HEAD commit in the viewport.

h is navigate_left globally; in the list view it has no left/right semantics so the scope reuses it for “jump to HEAD”.

Inherits from: [keybind] (global).

ActionDefaultWhat it does
revertvOpen the Revert confirmation dialog for the focused commit.

v globally is clean_untracked (uncommitted view). In commit-detail there’s nothing to clean, so the scope re-purposes v for revert matches the action shown in the Git Actions panel (Revert (v)).

Inherits from: [keybind] (global). Applies on any tab inside an open PR.

ActionDefaultWhat it does
reloadrReload PR data from the GitHub API.
approveaSubmit an approving review.
request_changesxSubmit a changes-requested review.
mergemOpen the merge dialog.
open_in_browseroOpen the PR’s URL via xdg-open / open.
labels_pickerlOpen the labels picker.
reviewers_pickervOpen the reviewers picker.
close_prctrl-xClose the PR (confirmation).
reopen_prctrl-oReopen a closed PR.
toggle_draftctrl-dToggle ready ↔ draft state.

Inherits from: [keybind.scope.pr][keybind].

ActionDefaultWhat it does
new_prnOpen the new-PR compose form.

[keybind.scope.pr.conversation], PR Conversation tab

Section titled “[keybind.scope.pr.conversation], PR Conversation tab”

Inherits from: [keybind.scope.pr][keybind].

ActionDefaultWhat it does
new_commentcOpen the comment editor overlay.
quote_replyshift-rQuote-reply to the focused comment.
edit_owneEdit your own focused comment.
delete_owndDelete your own focused comment (confirmation).
react+Open the emoji picker on the focused comment.

Inherits from: [keybind] (global). Applies on any tab of an open issue.

ActionDefaultWhat it does
reloadrReload issue data from the GitHub API.
labels_pickerlOpen the labels picker.
assignees_pickeraOpen the assignees picker.
milestone_pickermOpen the milestone picker.
close_or_reopenxClose an open issue / reopen a closed one (confirmation).
open_in_browseroOpen the issue’s URL in the browser.

Inherits from: [keybind.scope.issues][keybind].

ActionDefaultWhat it does
new_issuenOpen the new-issue compose form.

[keybind.scope.issues.detail], Issue Conversation / Timeline

Section titled “[keybind.scope.issues.detail], Issue Conversation / Timeline”

Inherits from: [keybind.scope.issues][keybind].

ActionDefaultWhat it does
new_commentcOpen the comment editor overlay.
quote_replyshift-rQuote-reply to the focused comment.
reference_in_new_issueshift-nOpen Compose pre-filled with a back-reference link.
edit_owneEdit your own focused comment.
delete_owndDelete your own focused comment (confirmation).
react+Open the emoji picker on the focused comment.

[keybind.scope.rebase], Interactive rebase plan editor

Section titled “[keybind.scope.rebase], Interactive rebase plan editor”

Inherits from: [keybind] (global).

ActionDefaultWhat it does
pickpPick, keep the focused commit.
rewordrReword, keep but open the inline subject editor.
editeEdit, pause the rebase at this commit.
squashsSquash into the previous commit, keep both messages.
fixupfFixup, combine + drop this message.
dropdDrop, remove the commit.
grabspaceGrab the focused row to reorder it with ↑↓.
abort_previousshift-aAbort a stale rebase that’s still active on disk.

[keybind.scope.rebase.resume], Resume prompt

Section titled “[keybind.scope.rebase.resume], Resume prompt”

Inherits from: [keybind.scope.rebase][keybind]. Shown when a rebase is paused.

ActionDefaultWhat it does
continue_rebasec shift-cgit rebase --continue.
skip_commits shift-sgit rebase --skip.
aborta shift-agit rebase --abort.

[keybind.scope.rebase.reword_editor], Reword inline editor

Section titled “[keybind.scope.rebase.reword_editor], Reword inline editor”

Inherits from: [keybind.scope.rebase][keybind]. Word-jump keys inside the inline subject editor.

ActionDefaultWhat it does
delete_word_leftctrl-h ctrl-w ctrl-backspaceDelete one word to the left.
delete_word_rightctrl-deleteDelete one word to the right.
word_leftctrl-leftJump cursor one word left.
word_rightctrl-rightJump cursor one word right.

[keybind.scope.conflict], 3-way conflict editor

Section titled “[keybind.scope.conflict], 3-way conflict editor”

Inherits from: [keybind] (global).

ActionDefaultWhat it does
pick_oursoPick OURS for the focused hunk.
pick_theirstPick THEIRS.
pick_both_ours_firstbKeep BOTH, OURS lines first.
pick_both_theirs_firstshift-bKeep BOTH, THEIRS lines first.
next_hunknMove to the next conflict hunk.
prev_hunkpMove to the previous conflict hunk.

Inherits from: [keybind] (global). Shared by new PR / new Issue / new comment / reply editors.

ActionDefaultWhat it does
submitctrl-sSubmit the compose form to the GitHub API.

The mention popup (#), word-jump editing keys, and Esc to cancel stay hardcoded, they’re shared text-input semantics, not view actions.

Keep h/j/k/l pure for navigation, move scrolling off the home row.

[keybind]
half_page_up = ["ctrl-u"]
half_page_down = ["ctrl-d"]
[keybind.scope.list]
# Free `h` if you don't want the HEAD-jump action. An empty array drops
# it, the global `navigate_left` (still `h`) wins.
select_head_commit = []

gitoui validates the full keybind tree at boot and refuses to start on:

  • Duplicate key in the same scope, a bound to two different actions under the same [keybind.scope.<path>].
  • Unknown action name, typo in a TOML key.
  • Unknown key spelling, e.g. superkey-a.
  • More than one modifier, ctrl-shift-a.

The diagnostic block points at the offending line, column, scope, and expected schema, same style as the TOML-syntax + theme validators.