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.
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.
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
The Magit Status
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
4 for the selected one.
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.
p will move between the next and previous section (such as a hunk);
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
- 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
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.
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.
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
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 (
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 (
xwill reset your head to the selected commit
vwill revert the commit
dwill diff between the selected commit and your working tree
awill apply the selected commit's changes to to your working tree
Awill cherry pick the commit on top of your working tree
Ewill interactively rebase from HEAD to the selected commit. Very useful if you want to rewrite history
C-wwill copy the commit hash to the kill ring
SPCwill 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-p magit will automatically show the commit in a separate window.
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.
A few years ago I wrote Effective Editing I: Movement and in that article I included a bunch of code for a feature I called “Smart Scan.” Back then I didn’t bother putting it on Github so I just left it as source code embedded in the article. I’ve now realized that hundreds have stuck it in their .emacs file but without the benefit of any potential updates, and with no way to actually contribute to the package.
Basically, Smart Scan let’s you jump between symbols in your current buffer that matches the one point is on. It does it unintrusively and without any prompts or other fancy UI gimmicks. Simply put your point on a symbol you want to search for in your buffer and type either
M-p to move forward or backward respectively. Give it a shot. I have moved it Github here. Patches welcome Enjoy!
A long while ago I wrote an article about Working with Multiple files in Dired. In the article I describe how you can work with files across multiple directories — for instance all the matches from a
find call — in a
But wouldn’t it be great if you can combine the text editing capabilities of Emacs on the files themselves? Well, if you’re a long-time reader of this blog, you will recognize the rhetorical nature of the question and know instinctively that, yes, it is indeed possible.
WDired — meaning writable dired — has been part of Emacs for a long time now. This feature is another hidden gem of Emacs, only briefly mentioned in a sub node in the
dired info manual.
Its mode of operation is simple: if any
dired or dired-derived buffer is switched to writable from read-only with the globally-recognized binding
C-x C-q, you can edit the dired buffer as though it were an ordinary buffer. If you are in editable dired mode the modeline for the dired buffer will say
Any change you make to the buffer will remain unchanged until you commit them by typing
C-c C-c. To cancel the changes and revert to the original state you can type
By default only the filename is editable, and for most that is good enough. There are, however, a couple of switches that let you change more than just the filename. If you set
t you can also edit the permission bits directly, although wdired will ensure you can only enter valid permission bits. If you want a free-form permission field without the system handholding, you can set it to
Another feature of wdired is its ability to rewrite symlinks. By default
wdired-allow-to-redirect-links is set to
t, meaning you can by default change the symlinks in Editable mode.
Two useful “safety” variables are
wdired-use-interactive-rename which if set to
t will prompt for every filename change you have made you when you commit the changes with
C-c C-c, and
wdired-confirm-overwrite asks if you want to overwrite files if your altered filenames conflict with existing files.
If you are a keen user of Emacs macros you may want to configure
wdired-use-dired-vertical-movement as it governs how where the point is put when you navigate up and down the dired list. You can set it to one of three switches:
nil, meaning it will not do anything out of the ordinary;
sometimes, meaning Emacs will move the point to the beginning of filename if the point is before it; and
t, meaning Emacs will always, unequivocally move the point to the beginning of the filename, mirroring the behavior in normal dired mode.
Probably the most useful part of WDired is the ability to edit filenames as though they were ordinary lines of text in a buffer. Most commands work as you would expect, including rectangle functions! Only the filename portion of text killed with the rectangle kill command
C-x r k are killed. WDired is fairly intelligent and will only let you edit portions that make sense: you cannot change the file size, for instance, as that portion of the buffer is set to read only.
Replace regexp works well, but don’t expect
^foo to work as the filename is not actually at the beginning of file. Provided you express your regexp so it only affects the filename-portion of the dired buffer, you’re golden. This is a slightly annoying limitation but one worth living with. The functionality afforded by WDired is amazing: regexp replace,
[upcase/capitalize]-word; elaborate macros — they all work.