Magit is not a thin wrapper around Git. It is a complete reimagining of the Git workflow inside a text editor, and it is almost universally cited as the single Emacs package that converts sceptics. After one week with Magit, most developers find the command-line Git UX intolerable by comparison.
This guide covers installation, the core status/staging workflow, branching, interactive rebasing, stashing, bisect, and a set of keybindings worth committing to muscle memory. It assumes Emacs 29+ and Git 2.40+.
Installation
;; With use-package + straight.el (recommended)
(use-package magit
:straight t
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch))
:custom
(magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)
(magit-save-repository-buffers 'dontask))
Or with the built-in package.el:
M-x package-install RET magit RETAfter installation, open any file inside a Git repository and press C-x g to open the status buffer.
The Status Buffer: Your Command Centre
The Magit status buffer replaces git status, git diff, and most of your daily git add calls. The layout:
Head: main Refactor user service
Merge: origin/main origin/main
Push: origin/main
Untracked files (1)
src/NewFile.php
Unstaged changes (2)
modified src/UserService.php
modified src/UserRepository.php
Staged changes (1)
modified src/Config.php
Key bindings inside the status buffer:
| Key | Action |
|---|---|
| TAB | Expand/collapse section or file diff inline |
| s | Stage file or hunk at point |
| u | Unstage file or hunk at point |
| S | Stage all unstaged changes |
| U | Unstage all staged changes |
| k | Discard change at point |
| g | Refresh the status buffer |
| q | Quit the buffer |
Hunk-Level Staging: Magit's Killer Feature
Move point to an unstaged file and press TAB to expand the diff inline. Navigate into a hunk and press s to stage just that hunk. Press C-SPC to set a region within a hunk and s to stage only those lines — partial hunk staging that git add -p makes painful.
;; Useful additions for diff navigation
(use-package magit
:bind (:map magit-hunk-section-map
("RET" . magit-diff-visit-file-worktree)))
Committing
Press c in the status buffer to open the commit popup (magit-commit). Common options:
| Key sequence | Action |
|---|---|
| c c | Create a new commit (opens commit message buffer) |
| c a | Amend the last commit |
| c e | Extend the last commit (amend without editing message) |
| c f | Create a fixup commit targeting another commit |
| c s | Create a squash commit targeting another commit |
| c w | Reword the last commit message |
Inside the commit message buffer, C-c C-c confirms, C-c C-k aborts. Magit automatically formats the buffer with a ruler at column 72, enforces a blank line after the subject, and shows the staged diff below for reference.
Branching and Checkout
Press b for the branch popup:
| Key sequence | Action |
|---|---|
| b b | Checkout a branch |
| b c | Create and checkout a new branch |
| b l | List all branches |
| b d | Delete a branch |
| b r | Rename a branch |
Magit offers completion for branch names using your configured completing-read framework (Vertico, Ivy, etc.), so switching branches is a fuzzy-search away.
Fetching, Pulling, Pushing
Press f for fetch, F for pull, P for push. The popups show the active remote and branch, so a simple f u fetches upstream and P u pushes to upstream. Force-push is available as P -f u — the -f flag is a transient argument, visible in the popup UI, which makes force-pushes deliberate rather than accidental.
Interactive Rebase
Interactive rebase is where Magit genuinely surpasses the command line. Press r for the rebase popup:
| Key sequence | Action |
|---|---|
| r i | Interactive rebase (opens rebase todo buffer) |
| r u | Rebase current branch onto upstream |
| r e | Rebase and edit every commit message |
| r s | Rebase and autosquash fixup/squash commits |
In the rebase todo buffer, place the cursor on a commit and press:
| Key | Action |
|---|---|
| r | Reword this commit's message |
| e | Edit (stop and drop to shell for this commit) |
| s | Squash into previous commit |
| f | Fixup (squash, discard this commit's message) |
| d | Drop this commit |
| M-p / M-n | Move commit up/down in the list |
Confirm with C-c C-c. Abort with C-c C-k. Magit automatically stashes uncommitted changes before the rebase and reapplies them after.
Log, Show, and Blame
Press l for the log popup. l l shows the log for the current branch. In the log buffer:
- RET — show the full commit diff
- a — cherry-pick the commit at point
- A — cherry-pick and immediately commit
- r i — interactive rebase starting from the commit at point
For blame, open any file and run M-x magit-blame (or bind it). Blame overlays each line with commit metadata. Press RET on a blame annotation to recurse into the parent commit's blame — a feature that has no equivalent in plain Git.
Stashing
Press z for the stash popup:
| Key sequence | Action |
|---|---|
| z z | Stash all changes (with message prompt) |
| z i | Stash index only |
| z p | Pop most recent stash |
| z a | Apply a stash (keep it in the list) |
| z l | List all stashes |
Bisect
Git bisect is underused because the command-line interface is clunky. In Magit:
- Open the log (l l).
- Press B to open the bisect popup.
- B s — mark current commit as bad.
- Move point to a known good commit and press B g — mark as good.
- Magit checks out the midpoint. Test, then B b (bad) or B g (good). Repeat.
- B r — reset when done.
Recommended Configuration
(use-package magit
:straight t
:bind (("C-x g" . magit-status)
("C-x M-g" . magit-dispatch)
("C-c g b" . magit-blame))
:custom
;; Show status in same window, avoid split
(magit-display-buffer-function
#'magit-display-buffer-same-window-except-diff-v1)
;; Save modified buffers without asking
(magit-save-repository-buffers 'dontask)
;; Spell-check commit messages
(git-commit-summary-max-length 72)
;; Auto-revert Magit buffers
(magit-auto-revert-mode t)
;; Show fine-grained word diffs
(magit-diff-refine-hunk 'all)
:hook
(git-commit-setup . git-commit-turn-on-flyspell))
Magit Forge: GitHub and GitLab Inside Emacs
Install forge to manage pull requests, issues, and notifications without leaving Emacs:
(use-package forge
:straight t
:after magit)
After adding your token to ~/.authinfo.gpg, press N in the status buffer to access the forge popup. N p l lists open PRs; N i l lists issues. You can create, review, and merge PRs entirely from the Magit buffer.
Summary Checklist
- Install Magit and bind
magit-statusto C-x g. - Use TAB to expand diffs inline; stage hunks with s, not whole files.
- Learn c c, c a, c f for the full commit workflow.
- Use r i for interactive rebase — it is dramatically better than the command line.
- Enable
magit-diff-refine-hunkfor word-level diff highlighting. - Add
forgeif you use GitHub or GitLab; it eliminates browser context switches. - Use B (bisect) for regressions rather than manually checking out commits.