Magit's performance isn't so good to say the least. I would go as far as to say that mediocre and in certain situations outright bad performance currently is Magit's biggest problem.
We have made some improvements in the past, like e.g. caching calls to git during a buffer refresh, but these improvements are to a large extend outweighed by the addition of new features. Already there exist useful features that are disabled by default because they can be to costly in some cases.
One major difference between Git and Magit is that the latter shows all kinds of useful information up-front. The major disadvantage of Magit's approach is that this information has to be always up-to-date (else it couldn't be relied on), and that doing so costs time.
To achieve good performance the cost of keeping (primarily, but not only) the status buffer up-to-date has to be reduced. And in order to do so, some fundamental changes have to be made. Doing that will take a while because it makes it necessary to replace some core abstractions. The good news is that improving or even replacing those abstractions will have other benefits beside improved performance.
A These are the primary causes of bad performance:
After the user has performed some action, the complete status buffer is refreshed. And that is done by recreating its content from scratch. Every time. All of it.
The required information is collected by calling git many times. Until recently, there was no alternative, but now Emacs has a foreign function interface and so we can start using libgit2 now.
Expensive parsing (primarily diffs and logs) is done synchronously, even when we don't need the complete result immediately (because most of it is not visible until the user scrolls).
The results of expensive parsing are not being cached. (The result of calls to git are cached, but that mitigates the effect of (2), not (1).)
Many sections are created by inserting the output directly into the user-visible buffer, where it is then manipulated to look and feel the way we want to. Without changing that, it would be very hard to address (3) and (4).
B There are three primary paths for improving performance:
libgit2 instead of calling git over and over again.Closely related to (2), only update those parts of a buffer that actually need updating.
Delay updating buffers that are not visible.
Insert diffs in two steps. Get a list of the modified files. For files that are expanded get the actual diff and insert it.
Luckily these two paths can be tackled in parallel, because in most cases a given section is either created by inserting and then "washing" git output (A5) or by processing a list of items (or a single value) using Elisp and then inserting that as the sections content (A2).
So aside from this overview, these changes will also be discussed independently, in dedicated issues.
I'd love to help contribute on number 2. I've developed some decent familiarity with the section management functions. I'll keep an eye out for the new issue.
Just want to comment that this would be really cool, especially number 2: I regularly do source-rewriting on a fairly large project with rewriters which are a bit hit-or-miss (they'll false-positive and rewrite stuff they should not and the serialisation will rewrite slightly more than it should) so there needs to be a post-op filtering to create the actual commits (remove stuff or split changes across multiple commits)
add -p/checkout -p kinda work but the lack of context and the keyboard answers means it's easy to "go automatic" and end up e.g. reverting a change which should not have or staging one which shouldn't be.
Magit's diff-committed and diff-uncommitted are absolute game-changers there, they're absolutely stellar in UI and UX. Their issue however is that staging changes are slow, and the slowness becomes huge as the diff size grows, with a ~15kloc diff (wasn't kidding about the bigness) it takes about 1mn30 between me hitting stage and Emacs being interactive again (though this is on 24.5, I should probably update).
@xmo-odoo Have you tried magit-ediff-stage? Although it's not quite the same as [un-]staging directly from magit's status buffer, it avoids the regeneration of the status buffer per-stage (so will likely be far faster per-stage), while retaining better interactivity than stock git.
@braham-snyder I'm really not fond of ediff (though that may be because I've almost never used it so I find it odd), and the complete overview of the working copy is probably the thing I love most about magit's diff views.
One more observation. magit-refresh can turn out to be very time consuming, especially for *-refine-*-related stuff. Oftentimes, I have magit-status (or any other magit buffer) buffer not displayed (no window displaying any of them), but when I do changes to the code in a corresponding repository, magit-refresh is still invoked. How about teaching magit to detect whether buffer(s) which it wants to refresh are not displayed, and thus queue actual refreshing on each of them (via e.g. buffer-local facilities) until they are actually displayed?
I am planning to do that too. Added it to the description above.
There may be some good pointers over at gitstatus that can help also make magit faster. Although someone probably ought to add the multi-threading and caching improvements of gitstatusd to git itself.
It seems that everybody here is aware of these issues, but just for the record I must mention that:
magit-status takes 30 seconds to run on my fast machine.I installed magit yesterday and I was excited about it, but for now I switch back to git at command line because git status responds promptly for my small projects.
There are _three primary paths_ for improving performance:
_1. ...
2.
3.
4.
5._
Luckily these _two paths_ can be tackled in parallel...
Looks like this one is on an expanding front :).
These performance issue is still really annoying. Please fix this!
At gentoo the support for git.el will be removed sooner or later. So I really wanted to use magit, but it's absolutely unusable: stashing, staging, updating the view takes 20-40 seconds - on a Core i7 machine with 8 cores!
I really want to use emacs, but so it is not possible. Please - fix it! I will sponsor chocolate.
20-40 seconds
That's not the usual experience. Either you are using a humongous repository or something is broken for you that isn't broken for other people.
20-40 seconds
That's not the usual experience. Either you are using a humongous repository or something is broken for you that isn't broken for other people.
@tarsius Thanks for getting back to me. Whatever the issue was, luckily I cannot replicate that situation anymore! I am now using Magit everyday. Thank you!
OK, thank you for the feedback: I digged deeper: This happens, when you have gettext- / *.po files in your tree. If you dismiss all changes on these kind of files (we have about 20 in our repo), magit responses quite fast.
I tried also to start the emacs without init.el, so no special hooks are activated to open gettext files.
Is this a new bug or should I only add it here as hint for other users, which have the same issue.
Actually lets look at this a bit. 20 untracked (or ignored?) files isn't much at all.
So if you have those ~20 files, then it takes 20-40 seconds to do a simple refresh (C-g)? Please time it. And without them it takes less than a second?
Are these files being ignore by Git (i.e. is there such a rule in .gitignore)? If there wasn't such a rule, does adding one make the issue go away? Even if it does, if you just added such a rule now, then remove it again for the subsequent tests.
Does the Magit status buffer show any information about these files? (Is there a Ignored files or Untracked files section?) Does removing all magit-insert-*-files sections from magit-status-section-hook fix the issue? If so, then figure out which of these functions is the cause.
More input to this issue:
There are 20 git controller po-files in my repo
(check with $> git status | grep \.po)
changed: cs.po
changed: da.po
changed: de.po
changed: el.po
changed: en.po
changed: es.po
changed: fr.po
changed: hu.po
changed: it.po
changed: ja.po
changed: lv.po
changed: pl.po
changed: poqt_gui.pot
changed: pt.po
changed: ro.po
changed: ru.po
changed: tr.po
changed: uk.po
changed: zh_CN.po
changed: zh_TW.po
-> entering "g" at magit buffer
-> utimer -s
Elapsed Time: 0 days 00:00:39.198 (39.198 seconds)
$> git checkout cs.po da.po de.po el.po en.po es.po fr.po hu.po it.po ja.po
-> revert the first half of the files
changed: lv.po
changed: pl.po
changed: poqt_gui.pot
changed: pt.po
changed: ro.po
changed: ru.po
changed: tr.po
changed: uk.po
changed: zh_CN.po
changed: zh_TW.po
-> entering "g" at magit buffer
-> utimer -s
Elapsed Time: 0 days 00:00:23.259 (23.259 seconds)
$> git checkout da.po...
-> checkout the first half, so the second half of the files stay changed:
changed: lv.po
changed: pl.po
changed: poqt_gui.pot
changed: pt.po
changed: ro.po
changed: ru.po
changed: tr.po
changed: uk.po
changed: zh_CN.po
changed: zh_TW.po
$> git checkout *.po
How large are our files:
$> wc *.po
5643 24040 189701 cs.po
4661 17088 144576 da.po
5428 23581 186507 de.po
4691 19496 182921 el.po
5623 24424 183887 en.po
5831 26770 198447 es.po
5872 26756 200695 fr.po
4341 15857 135002 hu.po
5867 26355 199595 it.po
5318 18889 188949 ja.po
5559 23807 190925 lv.po
4662 18840 155765 pl.po
5866 26783 199455 pt.po
5744 25187 192231 ro.po
5864 25840 232415 ru.po
5758 24131 192257 tr.po
4810 17666 168804 uk.po
5280 18832 177455 zh_CN.po
5273 18809 177316 zh_TW.po
About 300 translations per language. A lot of translations are sentences.
There are 7 further .po files, which are not git tracked and added into the .gitignore. These files do not interfere.
At the moment I have in my magit-status.el
(defcustom magit-status-sections-hook
'(magit-insert-status-headers
magit-insert-merge-log
magit-insert-rebase-sequence
magit-insert-am-sequence
magit-insert-sequencer-sequence
magit-insert-bisect-output
magit-insert-bisect-rest
magit-insert-bisect-log
magit-insert-untracked-files
magit-insert-unstaged-changes
magit-insert-staged-changes
magit-insert-stashes
magit-insert-unpushed-to-pushremote
magit-insert-unpushed-to-upstream-or-recent
magit-insert-unpulled-from-pushremote
magit-insert-unpulled-from-upstream)
When I remove
magit-insert-stashes
magit-insert-unpushed-to-pushremote
magit-insert-unpushed-to-upstream-or-recent
magit-insert-unpulled-from-pushremote
magit-insert-unpulled-from-upstream
Then I can reduce the time from 38 to 10 seconds
I just remembered we recently added something to make this easier: set magit-debug-refresh to t and then post the output from *Messages*.
Here's what those numbers normally look like:
Refreshing magit...
Running magit-pre-refresh-hook...done (0.002s)
Refreshing buffer `magit: magit'...
magit-insert-error-header 5.28e-06
magit-insert-diff-filter-header 3.5779e-05
magit-insert-head-branch-header 0.005917256
magit-insert-upstream-branch-header 0.009664677
magit-insert-push-branch-header 0.00295999
magit-insert-tags-header 0.029366055
magit-insert-status-headers 0.051382107
magit-insert-merge-log 0.002367351
magit-insert-rebase-sequence 0.000558741
magit-insert-am-sequence 0.000267262
magit-insert-sequencer-sequence 0.000472803
magit-insert-bisect-output 0.000263354
magit-insert-bisect-rest 5.3672e-05
magit-insert-bisect-log 5.2426e-05
magit-insert-untracked-files 0.002597223
magit-insert-unstaged-changes 0.003391459
magit-insert-staged-changes 0.005237672
magit-insert-stashes 0.020132359
magit-insert-modules 0.002975827
magit-insert-worktrees 0.002223353
magit-insert-unpushed-to-pushremote 0.041945618
magit-insert-unpushed-to-upstream-or-recent 0.032749889
magit-insert-unpulled-from-pushremote 0.000270675
magit-insert-unpulled-from-upstream 0.021647526
forge-insert-pullreqs 0.021850482
forge-insert-issues 0.015691664
Refreshing buffer `magit: magit'...done (0.236s)
Running magit-post-refresh-hook...done (0.008s)
Refreshing magit...done (0.253s, cached 84/116)
FYI I don't see magit-debug-refresh in the MELPA version.
FWIW, refresh takes about 400ms in my repo and I don't have forge enabled. Though I just disabled magit-todos and it brought it down to 200ms... yuck.
Edit: Is there a way to limit what gets refreshed when staging and unstaging? Ideally it'd only refresh the staged and unstaged sections and it'd be great to disable things like magit-todos during that refresh.
FYI I don't see
magit-debug-refreshin the MELPA version.
I think that was meant to be magit-refresh-verbose.
Ah, thanks. Looks like forge still takes a little time even if the repo isn't forge enabled yet, and magit-todos (specifically magit-todos-branch-list) is very expensive.
Refreshing buffer ‘magit: iheartjane’...
magit-insert-error-header 2e-06
magit-insert-diff-filter-header 6e-06
magit-insert-head-branch-header 0.015213
magit-insert-upstream-branch-header 0.020369
magit-insert-push-branch-header 0.004845
magit-insert-tags-header 0.011144
magit-insert-status-headers 0.057213
magit-insert-merge-log 0.004333
magit-insert-rebase-sequence 0.000206
magit-insert-am-sequence 9.9e-05
magit-insert-sequencer-sequence 0.000195
magit-insert-bisect-output 0.000101
magit-insert-bisect-rest 2.6e-05
magit-insert-bisect-log 2.6e-05
magit-insert-untracked-files 0.02154
magit-insert-unstaged-changes 0.018993
magit-insert-staged-changes 0.010953
magit-todos--insert-todos 0.158363
magit-insert-stashes 0.006123
magit-insert-unpushed-to-pushremote 0.026288
magit-insert-unpushed-to-upstream 0.011508000000000001
magit-insert-unpulled-from-pushremote 8.9e-05
magit-insert-unpulled-from-upstream 0.006936
forge-insert-pullreqs 0.042196
forge-insert-issues 5e-06
Refreshing buffer ‘magit: iheartjane’...done (0.387s)
Repositories with large amount of both files and changes per commit, e.g. produced by generated code (e.g. when the source code is regenerated after an update to the code generator), make magit-status totally unusable. I know me and others already complained about it many times and suggested a number of improvements. However, I have a feeling that implementing the comprehensive list of improvements from the top might take a lot of time. It's also not clear what's the current status of this sub-project.
Long story short, not sure if the following idea is implied by the comprehensive list of improvements at the top but I'd still mention it. I believe a drastic performance improvement for magit-status could already be achieved if all the sections that potentially insert diffs are eliminated and only lists of the corresponding files affected per section are displayed instead. This solves three main performance problems:
However, doing only that would spoil usability too indeed. The idea is to remap the key that is currently expanding diffs (which are already inserted in the buffer) to a function that would instead create a separate buffer where sections of diff hunks are inserted for that particular file only and immediately focus it; upon quit, focus back the parent magit-status buffer. So magit-status buffer acts as a "master" buffer towards any hunk buffers opened, e.g. when magit-status buffer is killed, all hunk buffers belonging to it are killed as well (at least by default). Whatever actions/features/bindings (e.g. (un)staging, reverting, etc.) are available in the magit-status buffer for the sections of diff hunks can be replicated in such separate file diff dedicated buffer. This essentially sounds like a feature request for an additional (buffer-wise) mode of operation for magit-section, which is planned to be released separately anyway as per #4003.
This does not sound like a lot of work and can be introduced as an optional mode to use for performance reasons. What do you guys think?
NOTE:
I seem to be having a performance issue with Magit on MacOS Catalina, it takes 0.6s to refresh a repository with one commit vs 0.033s on linux with the same repository. This increases to 1s on the main repository I work on and it therefore takes a while to perform any actions such as adding, removing files or committing.
I have read Magit's guide on performance for MacOS, and have tried various versions of emacs from 26.3 to master (28.0). I have also tried to find posts about the same issue, and have tried their solutions but they have not worked either (such as turning off themes). Most recommended upgrading the emacs version from 26.1 which I already had done though. This issue persists if I launch emacs only with magit enabled and in the config file.
I believe that it has something to do with git taking a long time to launch from emacs, which sounds similar to the fork vs vfork issue from emacs 26.1. I am not sure though how to check if my emacs version is actually calling vfork correctly.
Is there anything else I should try to debug? For now I only notice this problem in magit which is why I am posting it here and it seems to happen when only magit is enabled in the config file.
From the commandline, git executes as quickly as on linux. Two magit versions below:
Magit 20200307.319, Git 2.25.1, Emacs 26.3, gnu/linux
Magit 20200307.319, Git 2.25.1, Emacs 28.0.50, darwin
For comparison, here is the output for linux and mac after running magit-refresh on both linux and mac (on a repository with one file and one commit and similar hardware):
Linux Performance
Refreshing magit...
Running magit-pre-refresh-hook...done (0.009s)
Refreshing buffer ‘magit: newproject’...
magit-insert-error-header 1.653e-06
magit-insert-diff-filter-header 0.003189448
magit-insert-head-branch-header 0.002403502
magit-insert-upstream-branch-header 6.1721e-05
magit-insert-push-branch-header 3.5632e-05
magit-insert-tags-header 0.002833871
magit-insert-status-headers 0.011557551
magit-insert-merge-log 0.001288038
magit-insert-rebase-sequence 0.00017291
magit-insert-am-sequence 0.000111263
magit-insert-sequencer-sequence 0.000126705
magit-insert-bisect-output 9.7657e-05
magit-insert-bisect-rest 2.0356e-05
magit-insert-bisect-log 1.3118e-05
magit-insert-untracked-files 0.002335742
magit-insert-unstaged-changes 0.002110061
magit-insert-staged-changes 0.004152358
magit-insert-stashes 0.001360473
magit-insert-unpushed-to-pushremote 6.6282e-05
magit-insert-unpushed-to-upstream-or-recent 0.008802725
magit-insert-unpulled-from-pushremote 4.1933e-05
magit-insert-unpulled-from-upstream 1.104e-05
Refreshing buffer ‘magit: newproject’...done (0.037s)
Running magit-post-refresh-hook...done (0.000s)
Refreshing magit...done (0.049s, cached 50/73)
MacOS Performance
Refreshing magit...
Running magit-pre-refresh-hook...done (0.039s)
Refreshing buffer ‘magit: random’...
magit-insert-error-header 2e-06
magit-insert-diff-filter-header 0.055977
magit-insert-head-branch-header 0.057386
magit-insert-upstream-branch-header 5.3e-05
magit-insert-push-branch-header 3.4e-05
magit-insert-tags-header 0.057151
magit-insert-status-headers 0.199779
magit-insert-merge-log 0.02891
magit-insert-rebase-sequence 0.000172
magit-insert-am-sequence 8.1e-05
magit-insert-sequencer-sequence 0.000156
magit-insert-bisect-output 8.3e-05
magit-insert-bisect-rest 2.2e-05
magit-insert-bisect-log 2.2e-05
magit-insert-untracked-files 0.033737
magit-insert-unstaged-changes 0.02892
magit-insert-staged-changes 0.056712
magit-insert-stashes 0.029054
magit-insert-unpushed-to-pushremote 5e-05
magit-insert-unpushed-to-upstream-or-recent 0.143981
magit-insert-unpulled-from-pushremote 3.9e-05
magit-insert-unpulled-from-upstream 1.2e-05
Refreshing buffer ‘magit: random’...done (0.554s)
Running magit-post-refresh-hook...done (0.000s)
Refreshing magit...done (0.600s, cached 50/73)
I have actually messed around with it a bit more and think I have found a solution! For some reason setting magit-git-executable to the absolute path of git (/usr/local/bin/git) solved the problem, even if it was pointing to the exact same git executable (Checked using magit-debug-git-executable).
I know some other people were reporting the same problem, so I hope that solves it for them too.
Now I get the following new performance, which is 2x slower than Linux, but inline with the speed of git on MacOS vs Linux.
MacOS Performance
Refreshing buffer ‘magit: random’...
magit-insert-error-header 1e-06
magit-insert-diff-filter-header 2.9e-05
magit-insert-head-branch-header 0.008377
magit-insert-upstream-branch-header 4.7e-05
magit-insert-push-branch-header 3.8e-05
magit-insert-tags-header 0.007386
magit-insert-status-headers 0.021236
magit-insert-merge-log 0.003121
magit-insert-rebase-sequence 0.000118
magit-insert-am-sequence 6.8e-05
magit-insert-sequencer-sequence 0.000126
magit-insert-bisect-output 7.4e-05
magit-insert-bisect-rest 2.1e-05
magit-insert-bisect-log 2e-05
magit-insert-untracked-files 0.00416
magit-insert-unstaged-changes 0.003453
magit-insert-staged-changes 0.006959
magit-insert-stashes 0.003041
magit-insert-unpushed-to-pushremote 3.6e-05
magit-insert-unpushed-to-upstream-or-recent 0.01527
magit-insert-unpulled-from-pushremote 7e-05
magit-insert-unpulled-from-upstream 8e-06
Refreshing buffer ‘magit: random’...done (0.066s)
@ymherklotz I'm not personally experiencing this, but have experienced similar things before when my shell was slow to start because of junk I had in my .zshrc file. That isn't happening to you is it?
@ymherklotz In your comment you mentioned that setting the absolute path to git helped significantly.
I have actually messed around with it a bit more and think I have found a solution! For some reason setting
magit-git-executableto the absolute path of git (/usr/local/bin/git) solved the problem, even if it was pointing to the exact same git executable (Checked usingmagit-debug-git-executable).I know some other people were reporting the same problem, so I hope that solves it for them too.
Now I get the following new performance, which is 2x slower than Linux, but inline with the speed of
giton MacOS vs Linux.MacOS Performance
Refreshing buffer ‘magit: random’... magit-insert-error-header 1e-06 magit-insert-diff-filter-header 2.9e-05 magit-insert-head-branch-header 0.008377 magit-insert-upstream-branch-header 4.7e-05 magit-insert-push-branch-header 3.8e-05 magit-insert-tags-header 0.007386 magit-insert-status-headers 0.021236 magit-insert-merge-log 0.003121 magit-insert-rebase-sequence 0.000118 magit-insert-am-sequence 6.8e-05 magit-insert-sequencer-sequence 0.000126 magit-insert-bisect-output 7.4e-05 magit-insert-bisect-rest 2.1e-05 magit-insert-bisect-log 2e-05 magit-insert-untracked-files 0.00416 magit-insert-unstaged-changes 0.003453 magit-insert-staged-changes 0.006959 magit-insert-stashes 0.003041 magit-insert-unpushed-to-pushremote 3.6e-05 magit-insert-unpushed-to-upstream-or-recent 0.01527 magit-insert-unpulled-from-pushremote 7e-05 magit-insert-unpulled-from-upstream 8e-06 Refreshing buffer ‘magit: random’...done (0.066s)
How exactly did you set the magit-git-executable path? Do I just add
(setq magit-git-exectuable "/usr/local/bin/git")
to my emacs config?
I think my path was already set?
magit-git-executable: "/usr/local/bin/git" (2.26.2)
exec-path: ("/Users/stephenaghaulor/.nvm/versions/node/v12.7.0/bin/" "/Users/stephenaghaulor/.rbenv/shims/" "/usr/local/bin/" "/usr/bin/" "/bin/" "/usr/sbin/" "/sbin/" "/Library/Apple/usr/bin/" "/Users/stephenaghaulor/.local/bin/" "/Users/stephenaghaulor/.rbenv/bin/" "/Users/stephenaghaulor/src/gocode/bin/" "/Applications/Emacs.app/Contents/MacOS/bin-x86_64-10_14/" "/Applications/Emacs.app/Contents/MacOS/libexec-x86_64-10_14/" "/Applications/Emacs.app/Contents/MacOS/libexec/")
/Users/stephenaghaulor/.nvm/versions/node/v12.7.0/bin/ (t)
/Users/stephenaghaulor/.rbenv/shims/ (t)
/usr/local/bin/ (t)
/usr/local/bin/git (2.26.2)
/usr/bin/ (t)
/usr/bin/git (2.24.2 (Apple Git-127))
/bin/ (t)
/usr/sbin/ (t)
/sbin/ (t)
/Library/Apple/usr/bin/ (t)
/Users/stephenaghaulor/.local/bin/ (nil)
/Users/stephenaghaulor/.rbenv/bin/ (nil)
/Users/stephenaghaulor/src/gocode/bin/ (nil)
/Applications/Emacs.app/Contents/MacOS/bin-x86_64-10_14/ (t)
/Applications/Emacs.app/Contents/MacOS/libexec-x86_64-10_14/ (t)
/Applications/Emacs.app/Contents/MacOS/libexec/ (t)
I could really use some help, I'm seeing abysmal performance.
Refreshing buffer ‘magit: random’...
magit-insert-error-header 1e-06
magit-insert-diff-filter-header 0.005116
magit-insert-head-branch-header 0.033715
magit-insert-upstream-branch-header 0.302397
magit-insert-push-branch-header 7.9e-05
magit-insert-tags-header 0.414043
magit-insert-status-headers 0.763833
magit-insert-merge-log 0.004443
magit-insert-rebase-sequence 0.000231
magit-insert-am-sequence 0.00011
magit-insert-sequencer-sequence 0.000217
magit-insert-bisect-output 0.000115
magit-insert-bisect-rest 3.3e-05
magit-insert-bisect-log 3.3e-05
magit-insert-untracked-files 1.42289
magit-insert-unstaged-changes 0.971694
magit-insert-staged-changes 0.030547
magit-insert-stashes 0.062513
magit-insert-unpushed-to-pushremote 7.3e-05
magit-insert-unpushed-to-upstream-or-recent 0.098215
magit-insert-unpulled-from-pushremote 6.3e-05
magit-insert-unpulled-from-upstream 0.07922
Refreshing buffer ‘magit: random’...done (4.544s)
Please help.
EDIT:
The above stats are for a very large project. I profiled an empty git repo and saw comparable stats.
Refreshing buffer ‘magit: magit-test’...
magit-insert-status-headers 0.004225
magit-insert-merge-log 0.004057
magit-insert-rebase-sequence 0.000237
magit-insert-am-sequence 0.000109
magit-insert-sequencer-sequence 0.000224
magit-insert-bisect-output 0.000112
magit-insert-bisect-rest 3.2e-05
magit-insert-bisect-log 3.2e-05
magit-insert-untracked-files 0.014533
magit-insert-unstaged-changes 0.008059
magit-insert-staged-changes 0.00895
magit-insert-stashes 0.004064
magit-insert-unpushed-to-pushremote 0.004015
magit-insert-unpushed-to-upstream-or-recent 0.011885
magit-insert-unpulled-from-pushremote 2.9e-05
magit-insert-unpulled-from-upstream 6e-06
Refreshing buffer ‘magit: magit-test’...done (0.067s)
@saghaulor that's how I set that, but it didn't make a difference for me. What did help was disabling sections for the status buffer. In my case, the worst offenders were:
(remove-hook 'magit-status-sections-hook 'magit-insert-unpushed-to-pushremote)
(remove-hook 'magit-status-sections-hook 'magit-insert-unpushed-to-upstream-or-recent)
(remove-hook 'magit-status-sections-hook 'magit-insert-unpulled-from-pushremote)
(remove-hook 'magit-status-sections-hook 'magit-insert-unpulled-from-upstream)
Disabling sections is documented in the official manual (it's a single line at the bottom).
Any chance this will be addressed in the near term? I concur this is by far the biggest problem with magit. I absolutely love this tool, but for large repos it is approaching not worth using unfortunately.
magit-refresh-verbose was already mentioned in a previous comment over a year ago, but I guess I'm not the only one who missed that super helpful tip. I just saw it mentioned in this blog post via Sacha Chua's weekly newsletter, so hopefully that's of use to people reading here.
magit-refresh-verbosewas already mentioned in a previous comment over a year ago, but I guess I'm not the only one who missed that super helpful tip. I just saw it mentioned in this blog post via Sacha Chua's weekly newsletter, so hopefully that's of use to people reading here.
Thanks for the link, it was really helpful in my case.
Using magit was really slow on huge codes like Linux kernel. But after removing some hooks including above tips, I could get rid of time-consuming features which are unnecessary for my usages.
;; revision
(remove-hook 'magit-revision-sections-hook 'magit-insert-revision-headers)
(remove-hook 'magit-revision-sections-hook 'magit-insert-revision-notes)
(remove-hook 'magit-revision-sections-hook 'magit-insert-revision-diff)
(remove-hook 'magit-revision-sections-hook 'magit-insert-revision-tag)
(remove-hook 'magit-revision-sections-hook 'magit-insert-xref-buttons)
;; status
(remove-hook 'magit-status-sections-hook 'magit-insert-tags-header)
(remove-hook 'magit-status-sections-hook 'magit-insert-status-headers)
magit-insert-error-header 2.145e-06
magit-insert-diff-filter-header 0.007394417
magit-insert-head-branch-header 0.006269013
magit-insert-upstream-branch-header 1.054e-05
magit-insert-push-branch-header 9.621e-06
magit-insert-tags-header 0.014738266 !
magit-insert-status-headers 0.032404343 !!
magit-insert-merge-log 0.002848184
magit-insert-rebase-sequence 2.420365681 !!
magit-insert-am-sequence 0.000166788
magit-insert-sequencer-sequence 0.000218156
magit-insert-bisect-output 0.000103943
magit-insert-bisect-rest 2.768e-05
magit-insert-bisect-log 4.8204e-05
magit-insert-untracked-files 0.014268144 !
magit-insert-unstaged-changes 0.00530372
magit-insert-staged-changes 0.014727778 !
magit-insert-stashes 0.073646006 !!
magit-insert-unpushed-to-pushremote 5.2532e-05
magit-insert-unpushed-to-upstream-or-recent 0.019587845 !
magit-insert-unpulled-from-pushremote 3.435e-06
magit-insert-unpulled-from-upstream 3.128e-06
magit-insert-rebase-sequence is taking 2.5 seconds on a small-ish repo here with 2500 commits and 100 stashes
@Profpatsch do you have a rebase in progress? My magit-insert-rebase-sequence is 0.0002 seconds, on a repo with 41,307 commits and 100 stashes.
Refreshing buffer ‘magit: web’...
magit-insert-error-header 3e-06
magit-insert-diff-filter-header 0.018225 !
magit-insert-head-branch-header 0.022502 !
magit-insert-upstream-branch-header 0.023856 !
magit-insert-push-branch-header 0.009783
magit-insert-status-headers 0.086622 !!
magit-insert-merge-log 0.009021
magit-insert-rebase-sequence 0.000208
magit-insert-am-sequence 9.6e-05
magit-insert-sequencer-sequence 0.000189
magit-insert-bisect-output 9.9e-05
magit-insert-bisect-rest 3.1e-05
magit-insert-bisect-log 2.9e-05
magit-insert-untracked-files 0.047831 !!
magit-insert-unstaged-changes 0.013027 !
magit-insert-staged-changes 0.021695 !
magit-insert-stashes 0.091677 !!
magit-insert-unpushed-to-pushremote 7.2e-05
magit-insert-unpushed-to-upstream-or-recent 0.131429 !!
magit-insert-unpulled-from-pushremote 5e-06
magit-insert-unpulled-from-upstream 0.092876 !!
forge-insert-pullreqs 0.028664 !
forge-insert-issues 0.001083
Refreshing buffer ‘magit: web’...done (0.553s)
Running magit-post-refresh-hook...done (0.014s)
Refreshing magit...done (0.570s, cached 56/86 (65%))
@jdelStrother yes, of course, the function is a no-op without a rebase.
it’s not a big rebase either, I’m rebasing maybe 10 commits.
My initial profiling has pointed to https://github.com/magit/magit/blob/21454777281247d97814ce5fb64f4afe39fab5da/lisp/magit-sequence.el#L978-L1038 as the function taking most of the time.
If you want to do more profiling, you may find https://github.com/aspiers/etrace useful.
Most helpful comment
I seem to be having a performance issue with Magit on MacOS Catalina, it takes 0.6s to refresh a repository with one commit vs 0.033s on linux with the same repository. This increases to 1s on the main repository I work on and it therefore takes a while to perform any actions such as adding, removing files or committing.
I have read Magit's guide on performance for MacOS, and have tried various versions of emacs from 26.3 to master (28.0). I have also tried to find posts about the same issue, and have tried their solutions but they have not worked either (such as turning off themes). Most recommended upgrading the emacs version from 26.1 which I already had done though. This issue persists if I launch emacs only with magit enabled and in the config file.
I believe that it has something to do with git taking a long time to launch from emacs, which sounds similar to the
forkvsvforkissue from emacs 26.1. I am not sure though how to check if my emacs version is actually callingvforkcorrectly.Is there anything else I should try to debug? For now I only notice this problem in magit which is why I am posting it here and it seems to happen when only magit is enabled in the config file.
From the commandline, git executes as quickly as on linux. Two magit versions below:
For comparison, here is the output for linux and mac after running
magit-refreshon both linux and mac (on a repository with one file and one commit and similar hardware):Linux Performance
MacOS Performance