Skip to content

An introduction to Magit, an Emacs mode for Git

by mickey on December 6th, 2013

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.

28 Comments
  1. Charles Comstock permalink

    Nice overview of magit. It took me a little bit to understand this initially but Tab works as a show/hide toggle on each of the sections, files, as well as individual hunks of change.

    Another really useful feature is using + and – to change the hunk size making it easy to stage small changes into related commits.

    • mickey permalink

      I find selecting with the region what I want to stage is easier than trying to resize the hunk with +/-. Nice tip about TAB!

      • Jonas permalink

        Mickey, you might wanna mention TAB above, before M-1 and friends.

        As you said, it’s sometimes hard for a maintainer to see things from a user perspective, but one thing I can still distinctively remember, is that when I first began using Magit I paid way to much attention to learning how to use M-1 … and should instead just have used TAB.

  2. Awesome review. I’d like to see an “advanced” one too as I feel I’m not using much of magit’s power.

    Thanks

  3. Jonas permalink

    This is a very useful introduction to Magit. It’s better at introducing new users to Magit than what we have in the manual, so I am going to link to it from the README and possibly even from the info manual. As you have pointed out, documentation has to be improved in several ways, including but not limited to “discoverability”. I fully agree.

    It’s not that I have not worked on the documentation at all (cleaning up doc-strings was actually one of the first things I did when I got commit access), but for things that are/were still subject to change, I was a bit light on initial documentation, mostly because I don’t have the time to rewrite it several times. But also because I was quite happy with the new stuff not getting to much exposure, until the more adventurous users confirmed that it is useful (and that the interface makes sense to people beside myself).

    But your post motivates me to have a little break from coding and instead put some effort into improving the documentation (instead of delaying all of that until just before the release). I won’t focus on the new features, but more on the basic information and also the infrastructure (i.e. make a newer version of the info manual available on the web).

    You mention a few killer features (diff, log, “command console”), and I mostly agree that these are the “good parts”. Like other parts of Magit they still come with some quirks that go beyond just not being completely intuitive and lacking documentation. But when I started maintaining Magit they were in a comparatively good shape, specially considering the mess I found in the implementations that were later hacked on. So I have so far focused on improving the latter.

    Also the “mess elsewhere” in the past prevented my from working on the more useful parts, not only because the former grabbed my attention, but also because the accidental complexity prevented seemingly easy changes to core abstractions. The mess had leaked and what should have been straightforward changes would, without prior cleanup, have resulted in breakage everywhere.

    That has been mostly addressed now. There are still messy pieces, but for the most part they are self contained. Now that this is taken care of, many of the outstanding issue that I want to address before the release are related to these “killer features”.

    Logging will be improved by providing “better rev/ref/range/file completion”. Diffing will be improved by doing a single “washing cycle” which will allow addressing the numerous minor diff bugs.

    Currently I am working on making the “command console” more easily customizable. I have to disagree on the “command console” being “a fantastically well-done interface”; the idea is indeed great, but the implementation is severely lacking. There is a constant stream of feature request, where I have to respond “that isn’t possible yet”, because the implementation is very rigid, things that shouldn’t be are hardcoded. The new version allows defining new popups and changing bindings in existing ones, much more easily than before. It also makes it possible to set default actions and default arguments, which is a much asked for feature.

    And last but not least, and as a result of these changes, the new implementation can be easily used by external packages. I will however not make it available as a separate package. so for now external packages have to depend on all of Magit, even when they have nothing to do with Git. After the Magit release I will write a completely new package to replace not only the current implementation, but also the one I am working on right now. It will take the idea quite a bit further. You and others who like this interface and would like to use it elsewhere are welcome to do so, but should be aware that this is only an intermediate implementation, and that the api of the final idefix is likely going to be quite different.

    The other major objectives for the release are: support for multiple parallel git processes, bringing back a commit workflow that doesn’t involve emacsclient (but doesn’t suffer from the limitations of the legacy implementation), and simplifying the “section abstraction” (which would be an area where I myself have created some accidental complexity, I am afraid). And documentation.

    Integrating Yann’s magit-tramp, or more abstractly speaking “providing an api to uniformly address git objects”, probably won’t make it into the release. That will likely be one of the big changes in the next release. But some of the cleanup that will make that possible probably will be included already in this release.

    • mickey permalink

      Sounds great! I’m happy for you to do that.

      Well we can’t expect cutting edge documentation against a cutting edge build. The documentation for the stable version’s perfectly fine (but perhaps a bit light). Thankfully, if you know Git, most of it is fairly easy to figure out given enough time — but that’s basically true of Emacs. “Emergent use” of Emacs and its packages is what makes Emacs such a great tool; people go out and use it in ways you wouldn’t expect them to. I try to write about all the cool and hidden things and try and bring them to light. It’s also arguable if documentation should talk about “workflow” (which is a very personal thing) and instead just stick to explaining how it works. Bloggers write about how they use it and people can take that to heart, or not. It’s harder to really capture what people use it for when you’re the one writing the software. I have exactly the same problem with software I write (personally and commercially) — so it’s fine. At least there’s an info manual; that puts it above and beyond most projects!

      I think the biggest improvement — you’ve kind of already added it in master though — is not really knowing what a “command console” actually does when you’re randomly pressing keys to try and guess how to do something. I ended up hacking the magit console to display the little lisp symbol (merging, logging, diffing, etc.) in the actual command window until I got the hang of all the keys. In master the mode line says what it is so that’s fine.

      Better ranging would be awesome in reflog/log would be awesome and most welcome.

      One thing I was working on was a fix for master so you can invoke git mergetool against an active merge/rebase and have Emacs/Magit DWIM. Unfortunately due to the synchronous nature of mergetool waiting until the mergetool program closes, it would also hang Emacs — but running the command async made it easy for people to continue “doing stuff” against their git repo whilst the tool was running, which created all sorts of headaches. I never did fix the problem and I had to abandon it a few months ago:(

      I think you’re selling yourself short on the command console: it actually works great as a front-end, but I was working on pulling out the backend code so it was standalone, but that was a lot harder. But then again, you probably never expected people to want to use it for other things. I can envision an Emacs where this sort of interface is baked in and used for all sorts of things that previously required awkward universal arguments or annoying lisp variable switches. It’s a really great idea and it’s neatly implemented and it plays nice with Emacs’s hard-to-tame window system (this is the big one).

      I think copying org-mode’s TAB cycle system and have +/- glyphs next to files would clearly communicate that this is something that can be expanded/collapsed.

      Overall I think Magit’s a Killer App alongside Orgmode. It neatly solves the abstraction layer problem other git wrappers face and it does so without hiding the advanced features of Git.

      Thanks for the long post – it was very insightful. Keep up the great work!

      Mickey.

      • Jonas permalink

        I have to tone down my criticism of the current magit-key-mode. It’s not only a great idea, it also works very well. And has done so for a long time now, without requiring any major changes. But it does not lend itself to user customization and also not to the addition of new features. And the combination of these two limitations has caused me quite a bit of pain.

        I should also note that it wasn’t me who came up with the idea and wrote the current implementation. Phil Jackson has done that.

        Sometimes it’s hard to make the transition from ranting in commit messages (which I expect not to many people read) to commenting on a popular blog :-) So sorry Jack, magit-key-mode is great as is.

        Jack might not have envisioned his magit-key-mode as something that could be used elsewhere (but I might be wrong). However doing just that was actually the thing I wanted to work on the most every since I took over maintaining Magit. There just always were more pressing things to do, so early on I post phoned working on that until after the next release.

        Because I have decided not to work on this part of Magit I also rejected almost all such patches by contributors. I had decided not to spend any time improving magit-key-mode myself because I felt that it would be more beneficial to most users, if I spend my time elsewhere first. But that also meant that I didn’t want to spend any time explaining to contributors why the patches they proposes were, in my opinion, insufficient; and then work with them to improve their patches. I have had decided not to spend any time writing my own implementation, and so I also didn’t want to then spend that time to instead work with contributors to improve their implementations. Nevertheless I realize that must have been frustrating at times, and I apologize for that.

        I will replace magit-key-mode with magit-popup in a few days. But while that is a complete rewrite, it still is only an intermediate implementation. It makes the popups customizable – think magit-define-popup, magit-define-popup-switch, and such. But another major goal was to not change the interface in any significant way (some fixes to minor glitches are included though). So users that are not interested in customizing the popups should be mostly unaffected. I have decided to write this “missing link” now because the release is taking much longer than initially anticipated and I (and I assume many others) want these features *now*. Also I don’t want to have to reject anymore pull requests. And last but not least, the experience gained here will help, once I write idefix.

        So don’t expect too much just yet. magit-popup just implements the most often asked for features, but not much of my (and so it seems your) vision of what this could eventually turn into. The “command console” should indeed be though of as an “universal arguments on steroids” :-) And I tend to think that without an equivalent to interactive (or at least conventions on what to do inside interactive when called from the popup) the full potential cannot be reached, etc.

        Thanks for mentioning Magit alongside Org-Mode, that’s a big compliment! I’m hoping that one day Magit will become a package that, like Org-Mode, would encourage people to give Emacs a chance. But it’s still a long way to go and because getting there would require a lot of effort in areas that wouldn’t necessarily benefit current Emacs users much, I won’t tackle that anytime soon.

  4. Filip permalink

    What Emacs theme do you use?

    • mickey permalink

      The theme is a homegrown one, and predates Emacs’s theming library by a good ten years I’m afraid!

  5. John permalink

    Why is there no date on the comments?

    • mickey permalink

      Good question. I guess the theme doesn’t show them.

  6. John permalink

    @Flip If you are looking for a cool emacs theme and settings to use you can try https://github.com/skeeto/.emacs.d.

    • Filip permalink

      Thanks, but I just liked the theme in the screenshots :)

  7. Filip permalink

    Awesome blog BTW, I used it extensively to teach myself Emacs more than a year ago.

  8. John permalink

    I also forgot to say thanks for your post they were very helpful. I was already beginning to ask questions in the magit forum related to how to integrate git diff commandline with calling magit from emacsclient not knowing how wonderful a world of access existed with magit-status. And I’m sure it can only get better over time.

    Cheers!

  9. William DeMeo permalink

    Thanks for providing this guide. I tried to use it to learn how to extend a commit using magit, but was unable to do so.

    First, just a minor point, in your section “Committing Changes,” you say, “To open up the commit menu, type c.” You might want to indicate when/where this should be done. i.e., should one have first staged changes by typing s? Presumably one should be in the *magit* buffer when typing c. (Perhaps this is obvious.)

    Second, it’s not clear how any of the commit options you list are invoked. I learned by trial and error that you don’t want to just type the letter, e.g., e. Rather you must type -e.

    I am still unable to extend my previous commit using the magit interface. -e should allow me to commit with an empty commit message, but the commit fails with the error, “Aborting commit due to empty commit message.”

    Thanks for any help/suggestions you have. I’ll post again once I resolve this.

    • mickey permalink

      Hi William

      Yeah it’s assumed that the commands should be typed in the magit buffer.

      You don’t *have* to stage changes in order to commit; you can make git commit all unstaged changes also.

      The -e is “allow empty commit message” which is not the same as e for “extend.” It’s possible you have the older MELPA version and not the cutting edge found in github. It’s also possible the github head is busted – it does occasionally happen.

      • William DeMeo permalink

        Ah, okay, thank you. I’m fairly certain I have the standard version. I installed with package-install. The version you recommend in the intro of the webpage is MELPA. Perhaps there is a place on the page where you indicated that the options you’re describing are for the GitHub version. If so, I missed it. Anyway, thanks for the help, and for working on this much needed tutorial.

        • mickey permalink

          No problem. I mention at the top in the introduction that…

          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.

Trackbacks & Pingbacks

  1. Weekly review: Week ending December 6, 2013 » sacha chua :: living an awesome lifesacha chua :: living an awesome life
  2. Intro to Magit, Emacs mode for git | NeverFriday
  3. discover.el: discover more of Emacs using context menus | Mastering Emacs
  4. Tiny, tiny introduction to Magit « /dev/urandom
  5. How to list all lines with changes to easily navigate between changes with emacs? - Git Solutions - Developers Q & A
  6. My Emacs keybindings | Mastering Emacs

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS