Meta git (mgit)

 


Meta git (mgit) is a wrapper around magit, which is a wrapper around git.

If the output looks familiar, it's because it's the output from command git status -v. For the discerning eye, it uses the font-lock defaults from diff mode (notice the highlight on ;;). diff-mode uses repeated diff commands to highlight the refinements. Use diff-mode or diff-refine-hunk if you must have the eye-candy.


Motivation

Magit is a handy tool for working with git repository in GNU Emacs. However, if you work with a large enough repository, you'll hit a performance road-block. Here's an attempt to deconstruct the problem and find a solution.

Magit provides an information rich dashboard for git status. However, this comes with multiple calls to git command. If you run M-x magit-toggle-verbose-refresh and then run magit-status, you'll see the following output:

Refreshing buffer ‘magit: src<anand>’...
  magit-insert-error-header                          2.031e-06
  magit-insert-diff-filter-header                    3.0799e-05
  magit-insert-head-branch-header                    0.004475604
  magit-insert-upstream-branch-header                0.005282276
  magit-insert-push-branch-header                    3.5114e-05
  magit-insert-tags-header                           0.008421254
  magit-insert-status-headers                        0.021357499 !
  magit-insert-merge-log                             0.001540241
  magit-insert-rebase-sequence                       0.000193708
  magit-insert-am-sequence                           0.000190186
  magit-insert-sequencer-sequence                    0.000405629
  magit-insert-bisect-output                         0.000159796
  magit-insert-bisect-rest                           4.9848e-05
  magit-insert-bisect-log                            7.4688e-05
  magit-insert-untracked-files                       4.554816041 !!
  magit-insert-unstaged-changes                      3.27816181 !!
  magit-insert-staged-changes                        0.033723897 !!
  magit-insert-stashes                               0.009674576
  magit-insert-unpushed-to-pushremote                4.0493e-05
  magit-insert-unpushed-to-upstream-or-recent        0.014408128 !
  magit-insert-unpulled-from-pushremote              3.31e-06
  magit-insert-unpulled-from-upstream                0.004328423
Refreshing buffer ‘magit: src<anand>’...done (13.270s) 
 

In comparison, the output from git status -v provides pertinent information (see screenshot) in around 3-4s.


Plan 9

(Update: 6/6/2026) In Plan 9, mgit is available as a Javascript command which can be run via QuickJS.

qjs --std mgit.js 
  

Code: https://gitlab.com/atamariya/qjs/-/blob/js/mgit.js 

Notes: 

  • Some git features are only available in my fork of Plan 9 https://gitlab.com/atamariya/plan9front.git .
  • Processes running git/fs are likely to have differing views of the repository. e.g. a child process switching branch will make the parent process see the worktree as dirty.
  • In Plan 9 git, working with multiple remotes is not supported.
  • git/query -c c1:c2 returns a list of files changed between the two commits c1 and c2. It uses file hash for comparison.
# Switch branch
git/branch br 
 
# New branch from HEAD
git/branch -n br

# New branch from commit
git/branch -n -b commit br
 
# Pull latest from remote for the current branch
git/pull
 
# Fetch latest from remote (all remote branches)
# Note: doesn't update local branches. Both are equivalent. 
git/fetch origin
git/get git@gitlab.com:username/project.git

# If local branches are corrupted
rm -rf .git/refs/heads/*
 

 
# Add file (new or modified)
git/add file

# Remove file (both are equivalent)
git/add -r file
git/rm file

# Diff worktree against staging
git/diff
# List changed files
git/diff -n

# Diff staging against HEAD
git/diff -s
# List changed files
git/diff -ns

# Diff between two commits (used for displaying the commit details 
# by comparing against the parent)
git/diff -c parent:commit 

# Patch worktree (-r for reverse)
git/patch patch

# Patch staging (-r for reverse)
git/patch -s patch

# Commit to HEAD
git/commit -m 'commit message'

# Save worktree in stash
git/stash
# Stash list
git/stash -l
# Stash pop (apply recent stash to worktree and drop the same) 
git/stash -p
# Stash apply
git/stash -a n 
 
# Revert modifications in the worktree
git/revert 
  

 

Gitlab

If you manage multiple repositories in Gitlab, it might be easier to set up SSH keys rather than manage per-project based access tokens.

Plan 9: Generate keypair and load private key in factotum.

auth/rsagen -t 'service=ssh role=client' >key
cat key >/mnt/factotum/ctl
 
# Generate Public key 
auth/rsa2ssh key 
  

Register the public key with GitLab. Avatar → Edit profile → SSH Keys (left sidebar) → Add new key → paste the rsa2ssh output, give it a name and save.

If you already have a cloned repository, change the URL in .git/config as follows: 

[remote "origin"]
    url = git@gitlab.com:username/yourproject.git 
    

 

Lessons learnt

  1. Most git commands are fast except the ones which operate on tree level information. However, magit operations seem slower because of the refresh problem - it needs to refresh the complete dashboard.
  2. Performance bottleneck is not the number of files edited but the size of the repository (number of files in the repository).
  3. Stash saves the HEAD commit with the changes. This is used as the base for a three-way merge with the stash and the worktree.
     

Code (Work In Progress)

https://gitlab.com/atamariya/emacs/-/blob/mgit/lisp/vc/mgit.el

Patches for Magit

You need to apply following patches to magit files:
 
modified magit-diff.el
@@ -2324,6  +2327,11 @@
       (when orig
         (setq orig (magit-decode-git-path orig)))
-      (setq file (magit-decode-git-path file))
-      (setq header (nreverse header))
-      ;; KLUDGE `git-log' ignores `--no-prefix' when `-L' is used.
-      (when (and (derived-mode-p 'magit-log-mode)
-                 (seq-some (lambda (arg) (string-prefix-p "-L" arg))
-                           magit-buffer-log-args))
+      (when file
+    (setq file (magit-decode-git-path file)))
+      ;; KLUDGE `git-diff' ignores `--no-prefix' for new files and renames at
+      ;; least.  And `git-log' ignores `--no-prefix' when `-L' is used.
+      (when (or (and file (string-prefix-p "b/" file))
+        (and orig (string-prefix-p "a/" orig))
+                (and (derived-mode-p 'magit-log-mode)
+                     (--first (string-prefix-p "-L" it)
+                              magit-buffer-log-args)))
+        (when file
+      (setq file (substring file 2))) 
@@ -2341,5  +2327,8 @@
 (defun magit-diff-insert-file-section
    (file orig status modes rename header &optional long-status)
   (magit-insert-section section
-    (file file (or (equal status "deleted")
-                   (derived-mode-p 'magit-status-mode)))
+    (file (or file orig)
+      (or (equal status "deleted")
+              (derived-mode-p 'magit-status-mode)))
     (insert (propertize (format "%-10s %s" status
-                                (if (or (not orig) (equal orig file))
-                                    file
-                                  (format "%s -> %s" orig file)))
+                                (if (and orig file)
+                    (if (equal orig file)
+                    file
+                                      (format "%s -> %s" orig file))
+                  (or orig file)))
                         'font-lock-face 'magit-diff-file-heading))
 
 

 

Comments

Popular posts from this blog

Plan 9 : The Infinity Notebook

Plan 9: Quick Boot with UEFI

Comics Builder in GNU Emacs