Improve your workflow with Forge

Craft yourself a new weapon!
The Magit logo and the text 'Magit + Forge'.

The Magit logo was created by Prospect One, © Jonas Bernoulli. Licensed under CC BY-NC-SA 4.0 and available on GitHub. No changes were made to the logo.

Some tools are so good that once you start using them, you can't imagine not using them. Magit is one such tool. If you're also working with GitHub or GitLab, you can use Forge to make life even better.

In this post, I'll be giving a brief overview of what Forge is, how to get started (well, at least what's not in the manual), and the problems I ran into.

Magit and Forge

Magit is a 'Git porcelain inside Emacs'. In other words, it's a Git UI inside Emacs, providing access to repo status and a number of different commands. Magit is so good, that even if I used another editor for developing (hah, as if!), I'd still switch back to Emacs to handle my Git business.

Forge is a Magit extension that allows deeper integration with certain /forges/[1], such as GitHub and GitLab. For instance, when using GitLab, it allows you to create and comment on issues and pull requests, assign tasks, add labels (with auto-complete), open and close issues and more; all from within Emacs.

Motivation and early impressions

After posting this article to Reddit, I was asked about my motivation for using Forge over the GitLab UI. This section attempts to outline why I wanted to switch and what my impressions are after having used it actively for work for a couple of days.

Why not the GitLab UI?

I use GitLab for work, and while I think it's great in a lot of respects, it also has its share of issues, especially with the user interface. In general, navigating between projects, issue boards, and merge requests is very clunky and takes far too many clicks for it to be a good experience.

Because of this, I have bookmarked various projects' merge request overviews and issue boards, and use the bookmarks for navigating. It's not ideal, but it works tolerably well.

What doesn't work very well, however, is creating issues or merge requests. Not only do you need to write fairly long-form content, but you will also frequently want to add 'labels' or assign someone to a task. GitLab does have so-called /quick actions/, but they don't seem to work when used in the web UI.

This means writing and editing text in plain HTML text areas (without your favorite key bindings) and then having to use your mouse to navigate a UI, find multiple searchable lists of elements and filter and find the items you're looking for.

Overall: not a great experience.

Other options

As was pointed out in the Reddit thread, there are a number of ways to deal with the web UI issue. Notable contenders are the /Edit with Emacs/ extension for Chrome and Firefox, Emacs Application Framework (EAF) and it's browser, and Vimium and Surfingkeys.

I'm already a heavy user of Surfingkeys (though I realize I still have a lot of key bindings to internalize), and it's great for navigating your average website, but GitLab has so many things you can click on and interact with that it gets quite messy.

I tried Edit with Emacs, and while it's a really neat way to interact with text, it wasn't for me (for GitLab). I still had to activate it using my mouse, and it didn't relieve me from using the mouse to interact with the label and assignment menus either.

As for Emacs Application Framework, I'd never heard of it before. It looks super cool, but I'm a bit scared that if I go down that road, I'll never make it out alive. Further, this post by u/DarkNightened has some really good points on why the EAF might not be the right fit for this use case (as well as some pretty 🔥 Surfingkeys tips).

Early impressions

After having used Forge for a few more days, my impression is overwhelmingly positive. There are a few things it doesn't do, but I imagine some of that may be limited by the API that GitLab exposes. It also seems like GitHub receives more attention than GitLab, so it's quite possible that that integration has more features.

In the following sections, I'll be referring to issues and merge requests (MRs) collectively as topics, which is what Forge calls them.

The good parts

  • Issues and MRs in the Magit status buffer ::

This is the most basic thing that Forge gives you. In addition to just displaying these topics, it also displays labels (correctly color-coded) and whether a topic is open or not. If you expand an MR it'll list the commits it is made of. Viewing one of these items (by pressing <RET>) will also display assignees, all related comments and actions that have been taken.

  • Easy creation of issues and MRs from within Emacs ::

Of all the things that Forge does, this is what I wanted more than anything. I'm pleased to say that it works incredibly well. There are a few things that you can't do from Emacs (see the next section), but these are minor issues.

  • Autocomplete on typing when referencing issues and MRs, assigning users and labels ::

This one took me by surprise, but when you start typing out a topic ID (prefixed with # for issues or ! for MRs) you get auto-completion on topics in the project. The completion candidates show not only the ID, but also the topic title. This is super handy if you're adding related topics or otherwise want to reference them. This applies to seemingly all Magit buffers, including buffers for topic creation and for commit messages.

You'll also gain tab completion when assigning users to a topic or adding labels.

  • Quick copy a link to any topic, or open the topic in your browser ::

Opening a topic in your browser comes with a default key binding, and there is a function for adding the URL of the topic at point to your kill ring (forge-copy-url-at-point-as-kill). The latter wasn't bound to a key by default, but it's a very useful command when you need to share the link to a topic with a coworker or the like.

  • Viewing and checking out MRs is a breeze ::

It is incredibly easy to check out and create a branch from a merge request. Not much more to say about this, really. See the section on branching in the manual for more information.

The not-so-good parts

  • Certain values can't be set from Emacs ::

When creating merge requests, I haven't been able to specify that a merge request requires approval before being merged or that the source branch should be deleted after the merge. It's a minor thing, but it's something we do all the time on the team I'm on, so it'd be a nice feature to have.

  • Merge requests show diff between local branches ::

Say you make a few commits to the master branch (without pushing) before branching off, making a few more commits, and creating a merge request. Forge shows this MR as only containing the commits you made after branching off from master, even if the first commits you made to master haven't been pushed to the remote.

  • May (or may not) affect Magit performance ::

This is very uncertain and probably inaccurate. Magit has felt a bit slow over the past couple of days, but my machine is under quite heavy load, so I don't actually think it's been any slower than usual. All Forge updates should be performed asynchronously, so it shouldn't have a noticeable impact. However, when the minibuffer says 'pulling <repo>', my mind tends to automatically assume that Emacs isn't ready before the pull is successful. It does however relate to the next thing:

  • Updates to topics aren't 'optimistic' ::

When you make a change in Emacs, Forge goes ahead and tries to sync this with the remote right away, but until the sync is complete, the data you see in Emacs is still the same that it was before you edited it. This gives the impression that you must wait for a sync to finish before it's complete and that Emacs is busy updating, when it actually isn't. I'd love to see the Magit buffers update with your changes instantaneously, and then rather revert back (with an error message) if syncing with the remote failed.

Getting started

To get started, I'd recommend following the manual, which outlines most of what you need to know and shows you all the keybindings you need. That said, I did run into some issues (both on macOS and on NixOS), and there was some prerequisite knowledge I did not have, so I've decided to document that below.

Authentication: using .authinfo

The manual does have a section on getting started that includes some writing on setting up authentication, but, at least for GitLab, it requires some steps not mentioned in the manual.

Emacs uses ~/.authinfo files to handle credentials. I struggled to find much information about the format and what is required, but I can confirm that this entry format works:

machine gitlab.com/api/v4 login username^forge password api-token

Simply replace username and api-token with your username and your API-token (with 'api' scope), put it in ~/.authinfo, and you're off to the races. Admittedly, I don't know what the ^forge bit after the username means, but I assume it somehow relates it to Forge.

For more information on secrets and authentication, see this article on /Mastering Emacs/, which goes into quite a bit of detail of how to deal with encryption and credentials in Emacs.

macOS issues

Can't fetch topics

When setting it up on macOS, the biggest issue I ran into was that I couldn't successfully fetch topics (issues and pull requests). This appears to be a well-known bug with the ghub package that Forge uses, and this GitHub issue has a couple of workarounds for it. Personally, I had to force a workaround, by evaluating the following snippet:

(setq ghub-use-workaround-for-emacs-bug 'force)

However, I didn't put it in my config, and it seems to have worked successfully without setting this on subsequent uses, so it may or may not be necessary.

Recursive load

I also ran into a problem with window-purpose during the setup, causing an error about 'recursive load'. There is an issue on GitHub that describes it. One of the comments said to put

(require 'window-purpose)

in your .spacemacs file, which seemed to fix the issue for me.

This has not been an issue on NixOS.

NixOS issues: 'no EmacSQL SQLite binary available'

NixOS required much less fiddling to get set up, but there was an issue with my Emacs missing a binary, causing it to fail on startup. Specifically, I was told that there was 'no EmacSQL SQLite binary available', much like this issue about a missing C compiler. There may be a number of ways around it, but the solution I found was to change my installation of Emacs to one that includes the required emacsql-sqlite package. To do that, I replaced my Emacs listing in .configuration.nix with the following:

(emacsWithPackages (epkgs: [ epkgs.emacsql-sqlite ]))

I've only just started using Forge, but I've been very positively surprised by how much power it gives me directly from my favorite editor. Being able to check out and create branches from merge requests with a single command? Yes, please. Not having to take my hands off the keyboard to set issue labels? Perfect. Editing text and creating issues in an actual text editor and not having to copy it over into a browser window? You had me at Emacs.

I suspect there is still much to discover, though, so let's get going!

Footnotes

[1]

According to wikipedia, a forge is 'a web-based collaborative software platform'

[1]

According to wikipedia, a forge is 'a web-based collaborative software platform'



Thomas Heartman is a developer, writer, speaker, and one of those odd people who enjoy lifting heavy things and putting them back down again. Preferably with others. Doing his best to gain and share as much knowledge as possible.