An introduction to Magit, an Emacs mode for Git

If you’re lucky (or unlucky, depending on your viewpoint) enough to be using Git as part of your workflow, you may have come across Magit, an Emacs interface to Git. Magit’s an excellent interface to Git, but it does assume you know exactly what you’re doing with Magit and, ultimately, Git.

Magit has a fine Info manual that covers all the many actions Magit supports, but like a lot of manuals it doesn’t help with workflow; it assumes you’re already familiar with Magit and that you know exactly what to do and how to go about doing it.

Magit is also under active development. It pays to be on the cutting edge because as of Dec 2013 there is a forthcoming release that adds a lot of cool new stuff to Magit that makes it even better. Therefore, this tutorial will assume you’re using the bleeding edge.

To install the master branch version of Git, I suggest you use Melpa. Alternatively you can pull the latest from their github repository and follow their (simple) build instructions.

Here’s the first part of my tutorial on Magit. It will cover the status window; staging and unstaging items; committing changes; and the history view.

Getting Started

First and foremost: Magit does not hide the complexities of Git; in fact, you most certainly need to know exactly what Git is doing in order to truly use Magit effectively. I like to think of Magit as a tool that removes the tedium of interacting with Git’s rather poor commandline interface. It does this very, very well.

To use Magit, run M-x magit-status. This command will open up a window (or prompt you for a Git repository if the buffer’s file directory is not under Git control) and display Magit’s status screen. It is through this interface that you will use Magit. If you’re a user of Emacs VC then you must know that, frustratingly, Magit makes no effort to integrate itself into Emacs’s VC (Version Control) abstraction layer. Say what you want about VC, but it works across a wide range of source control systems and provides a unified interface to all of them. To use Magit you must do it through M-x magit-status.

The Magit Status

An example of Magit’s status window

The first thing you’ll notice about the Magit status window is that it plays nice with your window configuration when you open it, and when you close it with q. Almost all the actions you can carry out in Magit is done through a one-key system that opens up a “command console” window at the bottom, letting you further refine your command before you carry it out. This is a fantastically well-done interface and is probably the killer feature that makes Magit so great. I like it so much that I have copied the underlying interface code for use in some of my own Emacs hackery. It’s really, really nice.

The status window will give you an overview of your repository, including: staged/unstaged changes; unpulled/unpushed commits; stashes; and a header containing: local and optional remote, the latest tag, if any; and HEAD.

The older, stable version does little to aid discoverability, but in the bleeding edge you can type ? to get an annotated list of actions. I found it really difficult to use Magit in the beginning because I had to stumble my way through random menus until I found the thing I was looking for. The annotated list is, however, not complete. There are some commands (important ones, depending on your git workflow) missing from the list – particularly E, for interactive rebase.

Staging and Unstaging Items

Putting stuff into Git is something you’ll do often, and Magit has a selection of keybindings and tools to help you make that easier. The key take away is that you can stage “items” – not just the whole file, but the hunks in a diff, for selective staging. The killer feature here is how it displays this information, using its “levels” system. Magit lets you expand and collapse staged and unstaged files with TAB. For more granular control, you can use M-1 through to M-4 for all the files; and 1 to 4 for the selected one.

Magit showing diff hunks

Level 1 hides everything in a category (say, “staged” files); Level 2 expands to show just the filenames in a category (this is the default); Level 3 will show the git hunk headers; and Level 4 will show all the diff hunks. I use Levels 2 and 4 the most, but if you’re using TAB Magit will pretty much “do what you want.”

You have a range of keybinds available to make your life easier. n and p will move between the next and previous section (such as a hunk); M-n and M-p move between sibling sections, such as between each file in level 4, or between each section (like staged or unstaged). You can use + and - to enlarge or shrink each hunk and 0 to reset to the default. You can also type H to refine the hunk for additional diff highlighting. Pressing RET will go to the file where the change is made. It works on both hunks and files.

To stage or unstage you can type s or u to stage/unstage the item (be it a whole file, or just a hunk) – however, there’s one more very useful tip. If you use the region to select a portion of a hunk and then press stage/unstage then Magit will automatically stage or unstage just that selected region! That’s extremely useful for fine-grained control when a diff hunk itself is not good enough.

Finally, sometimes you’ve made changes you don’t care to ever commit; like staging and unstaging above, you can discard hunks and files (revert to HEAD) and delete untracked files from your filesystem. To do this, press k. This command works for more than just staging/unstaging – for instance, you can also remove a stash with it.

Committing Changes

To open up the commit menu, type c. You’ll be given a laundry list of switches, most of which you probably won’t need to use very often. What is useful are the actions. You can do much more than merely commit (c) staged changes:

  • You can extend (e) the current commit HEAD is pointing to
  • You can amend (a) the commit message
  • You can reword (r) it, if you don’t like the commit message
  • and you can both fixup (f) and squash (s) against the current commit. If you have previously markewd a commit with . this commit will be used instead.

Extending a commit basically envelops the changes you’re trying to stage into the current commit HEAD. So if you forgot to commit some stuff that belongs to the commit you just did – use extend. If you want to amend the commit message as well, use amend.

Rewording it will do so without committing your staged changes. Use this if you fat-fingered a commit message and you want to change it.

If you want to create a “fixup!” or “squash!” commit against the latest commit you have made – for later use with rebase and --autosquash – use the fixup or squash commands. If you’re not into rewriting your git history, and if you never use rebase, then these two commands are probably not very useful to you.

Logging

Magit’s History screen

I think one of the areas where Magit really shines is its multitude of switches to filter, sort and search your git history. Not only does it display this information, but it lets you act on it interactively. To access the log menu, type l.

The first handy shortcut you should know is l l. That opens the “short log”. In it, you will see a one-line commit message; the name of author; how long ago the commit happened; the tree structure of the git log; and various labels, like where HEAD is and where the branch markers are.

If you ever screw up, git reflog is there to save the day, and Magit does a stellar job overlaying a nice UI on top of the reflog mechanism (l h.)

Both the reflog and the normal log has a wealth of useful keybinds.

There are many things you can do with an individual commit in the log:

  • . will mark the commit for use with commands like commit fixup and commit squash (c f and c s)
  • x will reset your head to the selected commit
  • v will revert the commit
  • d will diff between the selected commit and your working tree
  • a will apply the selected commit’s changes to to your working tree
  • A will cherry pick the commit on top of your working tree
  • E will interactively rebase from HEAD to the selected commit. Very useful if you want to rewrite history
  • C-w will copy the commit hash to the kill ring
  • SPC will show the full commit message

A note on marking: the mark command will persist even if you close the log window. It’s a powerful tool but it’s easy to forget you’ve marked something.

If you navigate up/down the log with M-n and M-p magit will automatically show the commit in a separate window.

Conclusion

Magit’s a great tool for experienced Git users. If you’re new to Git, then Magit may help you make sense of how Git works, but it’ll never teach you Git. The only thing that impedes Magit, in my opinion, is the lack of discoverability; despite exposing a million different Git arguments, switches and toggles, it ironically does not teach you how to find and use itself. I found it quite hard to replace the Git commandline (not because I like it – I actually think it’s awful) but because the commands and actions I wanted to do were quite well-hidden. The cutting edge version is much better, with ? actually giving you an annotated list of (some, but still not all) the different key menus. But it’s a big improvement. If you’re on the fence about Magit, or if you’ve tried and failed to adopt it, I suggest you give it another go. I plan on covering more of Magit in future posts.