Skip to main content

9.2.2 Performance

After Magit has run git for side-effects, it also refreshes the current Magit buffer and the respective status buffer. This is necessary because otherwise outdated information might be displayed without the user noticing. Magit buffers are updated by recreating their content from scratch, which makes updating simpler and less error-prone, but also more costly. Keeping it simple and just re-creating everything from scratch is an old design decision and departing from that will require major refactoring.

I plan to do that in time for the next major release. I also intend to create logs and diffs asynchronously, which should also help a lot but also requires major refactoring.

Meanwhile you can tell Magit to only automatically refresh the current Magit buffer, but not the status buffer. If you do that, then the status buffer is only refreshed automatically if it is the current buffer.

(setq magit-refresh-status-buffer nil)

You should also check whether any third-party packages have added anything to magit-refresh-buffer-hook, magit-status-refresh-hook, magit-pre-refresh-hook, and magit-post-refresh-hook. If so, then check whether those additions impact performance significantly.

Magit can be told to refresh buffers verbosely using M-x magit-toggle-verbose-refresh. Enabling this helps figuring out which sections are bottlenecks. The additional output can be found in the *Messages* buffer.

Magit also reverts buffers for visited files located inside the current repository when the visited file changes on disk. That is implemented on top of auto-revert-mode from the built-in library autorevert. To figure out whether that impacts performance, check whether performance is significantly worse, when many buffers exist and/or when some buffers visit files using TRAMP. If so, then this should help.

(setq auto-revert-buffer-list-filter

For alternative approaches see Automatic Reverting of File-Visiting Buffers.

If you have enabled any features that are disabled by default, then you should check whether they impact performance significantly. It’s likely that they were not enabled by default because it is known that they reduce performance at least in large repositories.

If performance is only slow inside certain unusually large repositories, then you might want to disable certain features on a per-repository or per-repository-class basis only. See Per-Repository Configuration. For example it takes a long time to determine the next and current tag in repository with exceptional numbers of tags. It would therefore be a good idea to disable magit-insert-tags-headers, as explained at the mentioned node.

Microsoft Windows Performance:  
MacOS Performance:  

Log Performance

When showing logs, Magit limits the number of commits initially shown in the hope that this avoids unnecessary work. When using --graph is used, then this unfortunately does not have the desired effect for large histories. Junio, Git’s maintainer, said on the git mailing list ( "--graph wants to compute the whole history and the max-count only affects the output phase after --graph does its computation".

In other words, it’s not that Git is slow at outputting the differences, or that Magit is slow at parsing the output - the problem is that Git first goes outside and has a smoke.

We actually work around this issue by limiting the number of commits not only by using -<N> but by also using a range. But unfortunately that’s not always possible.

When more than a few thousand commits are shown, then the use of --graph can slow things down.

Using --color --graph is even slower. Magit uses code that is part of Emacs to turn control characters into faces. That code is pretty slow and this is quite noticeable when showing a log with many branches and merges. For that reason --color is not enabled by default anymore. Consider leaving it at that.

Diff Performance

If diffs are slow, then consider turning off some optional diff features by setting all or some of the following variables to nil: magit-diff-highlight-indentation, magit-diff-highlight-trailing, magit-diff-paint-whitespace, magit-diff-highlight-hunk-body, and magit-diff-refine-hunk.

When showing a commit instead of some arbitrary diff, then some additional information is displayed. Calculating this information can be quite expensive given certain circumstances. If looking at a commit using magit-revision-mode takes considerably more time than looking at the same commit in magit-diff-mode, then consider setting magit-revision-insert-related-refs to nil.

When you are often confronted with diffs that contain deleted files, then you might want to enable the --irreversible-delete argument. If you do that then diffs still show that a file was deleted but without also showing the complete deleted content of the file. This argument is not available by default, see (transient)Enabling and Disabling Suffixes. Once you have done that you should enable it and save that setting, see (transient)Saving Values. You should do this in both the diff (d) and the diff refresh (D) transient popups.

Refs Buffer Performance

When refreshing the "references buffer" is slow, then that’s usually because several hundred refs are being displayed. The best way to address that is to display fewer refs, obviously.

If you are not, or only mildly, interested in seeing the list of tags, then start by not displaying them:

(remove-hook 'magit-refs-sections-hook 'magit-insert-tags)

Then you should also make sure that the listed remote branches actually all exist. You can do so by pruning branches which no longer exist using f-pa.

Committing Performance

When you initiate a commit, then Magit by default automatically shows a diff of the changes you are about to commit. For large commits this can take a long time, which is especially distracting when you are committing large amounts of generated data which you don’t actually intend to inspect before committing. This behavior can be turned off using:

(remove-hook 'server-switch-hook 'magit-commit-diff)

Then you can type C-c C-d to show the diff when you actually want to see it, but only then. Alternatively you can leave the hook alone and just type C-g in those cases when it takes too long to generate the diff. If you do that, then you will end up with a broken diff buffer, but doing it this way has the advantage that you usually get to see the diff, which is useful because it increases the odds that you spot potential issues.

Microsoft Windows Performance

In order to update the status buffer, git has to be run a few dozen times. That is problematic on Microsoft Windows, because that operating system is exceptionally slow at starting processes. Sadly this is an issue that can only be fixed by Microsoft itself, and they don’t appear to be particularly interested in doing so.

Beside the subprocess issue, there are also other Windows-specific performance issues. Some of these have workarounds. The maintainers of "Git for Windows" try to improve performance on Windows. Always use the latest release in order to benefit from the latest performance tweaks. Magit too tries to work around some Windows-specific issues.

According to some sources, setting the following Git variables can also help.

git config --global core.preloadindex true   # default since v2.1
git config --global core.fscache true # default since v2.8
git config --global 256

You should also check whether an anti-virus program is affecting performance.

MacOS Performance

Before Emacs 26.1 child processes were created using fork on macOS. That needlessly copied GUI resources, which is expensive. The result was that forking took about 30 times as long on Darwin than on Linux, and because Magit starts many git processes that made quite a difference.

So make sure that you are using at least Emacs 26.1, in which case the faster vfork will be used. (The creation of child processes still takes about twice as long on Darwin compared to Linux.) See 1for more information.

  1. ERROR