I don't believe I said so on this bog before but I've more or less always used emacs as my default text editor since I was a baby linux user.

I recently started emacs a lot more, mainly from writing more lisps in my side projects at home, and as such have spent quite some time configuring it.

I've also somewhat recently, last year maybe, started using yadm to synchronize my dotfiles (for non-nerds, understand my configuration files) with git.

Unfortunately, those two things didn't merge so well…

Yet Another Dotfiles Manager

As the name may suggest, yadm is only one of the many ways to synchronize dotfiles. I don't feel strongly about using it but it fit two of my needs:

  1. Be unobtrusive
  2. Allow me to configure alternatives per host!

I currently use 4 computers on the regular, work excluded with varying screen sizes or performance. I'm nearsighted and my laptop font is not going to be the same than my desktop's. So this possibility is paramount.

As Yadm's authors say in their very documentation, it's better to allow for conditional syntaces inside your configuration file itself. Unfortunately, not every piece of software is the hubristic dream that is emacs. That's when the Yadm alternative feature comes in handy.

Anyway, yadm works for me so I'm gonna keep using it for a while.

Enter emacs

At first, I didn't really have a reason for using emacs. There were two big editors Vi(M) and emacs. I don't enjoy Vi's modal paradign. Maybe I could have gotten used to it, maybe not. That led me to emacs and that has been my choice for simple editing, defaulting to nano on some remote hosts, on occasion.

I tried from time to time to configure it, theme it, etc., but never really stuck to it. I mostly used what was recommended at work. And ended up running VSCode for my personnal projects in the last few years.

Then I started do write more lisp, mainly Common Lisp and Janet, and now my emacs configuration is split into a dozen files.

I will write more about Lisps and emacs another day.

Boom

At some point I configured Magit to use git with emacs. It's great. Honestly the best git UI I've ever used and I've only used it for a few weeks.

The issue is that yadm splits the working directory and the git directory. That means that tools that try to interact with git may have issues with yadm. And that's what happened to me and magit.

My first attempt at solving that problem, was to use TRAMP as recommended by Phil Jackson and a now-anonymous Reddit user. You now have a yadm function you can bind to call magit and make it work with yadm by making magit believe you're operating on a remote host (that's what TRAMP is for).

Here is the snippet if the blog disappears one day:

(use-package tramp
  :config
  (add-to-list 'tramp-methods
               '("yadm"
                 (tramp-login-program "yadm")
                 (tramp-login-args (("enter")))
                 (tramp-login-env (("SHELL") ("/bin/sh")))
                 (tramp-remote-shell "/bin/sh")
                 (tramp-remote-shell-args ("-c")))))

(defun yadm ()
  (interactive)
  (magit-status "/yadm::"))

That worked well. However, I sometimes just call git commit from a shell and I expect it to open up emacs and let me edit the commit, check diffs, etc. I vaguely tried to find ways to alter magit's behavior for just one directory and then spin up that function but it started to look dirtier and more unstable by the second.

And now, the solution

Then, by digging more into how yadm and magit worked, I ended up finding this Github comment from user noncog/Jake Turner that sets your git directory and work directory/tree on the fly when magit operates on your dotfiles directory.

Here's the final modification that operates on my home and yadm's default storage directory:

First, the fuction definition:

(defun my/magit-process-environment (env)
  "Detect and set git -bare repo env vars when in tracked dotfile directories."
  (let* ((default (file-name-as-directory (expand-file-name default-directory)))
         (git-dir (expand-file-name "~/.local/share/yadm/repo.git/"))
         (work-tree (expand-file-name "~/"))
         (dotfile-dirs
          (-map (apply-partially 'concat work-tree)
                (-uniq (-keep #'file-name-directory (split-string (shell-command-to-string
                (format "/usr/bin/git --git-dir=%s --work-tree=%s ls-tree --full-tree --name-only -r HEAD"
                        git-dir work-tree))))))))
    (push work-tree dotfile-dirs)
    (when (member default dotfile-dirs)
      (push (format "GIT_WORK_TREE=%s" work-tree) env)
      (push (format "GIT_DIR=%s" git-dir) env)))
  env)

It could be improved. I could probably use emacs' home variable to possibly make it work on other operating system. I could use an XDG variable for the yadm directory, or set an env variable, or something like that. I do not however currently have this use case which is why it's not done yet.

I also set it as a hook to run emacs has started enough. I will post my current magit use-package block but it's the :hook that you're looking for:

(use-package magit
  :ensure t
  :hook
  (after-save . magit-after-save-refresh-status)
  (emacs-startup-hook
   . (lambda () (advice-add 'magit-process-environment
			    :filter-return #'my/magit-process-environment)))
  :bind (:map magit-mode-map
	      ("q" . (lambda() (interactive) (magit-mode-bury-buffer t)))))

And now, magit works on "regular" repositories and on my dotfiles, managed with yadm. Stay tuned for more infinitesimal workflow improvements.