8

I have been using the setup described on the top comment here to manage my dotfiles using git. In short, I have this in my .bashrc:

alias config='/usr/bin/env git --git-dir=$HOME/.myconf/ --work-tree=$HOME'

I then can run any git command using config add <file>, etc. Is there anyway to get this working with magit, so I can commit changes to my configuration files without dropping into a shell?

4
  • Setting magit-git-global-arguments as a directory-local variable may be an option.
    – phils
    Commented Feb 10, 2017 at 10:57
  • Did you solve the issue? I also want to track my dot files via magit Commented Sep 5, 2018 at 7:15
  • Unfortunately not. This is an ongoing source of friction for me.
    – nbren12
    Commented Sep 6, 2018 at 16:32
  • The workaround I have settled on ismv .cfg .git, use Magit, mv .git .cfg. Commented Apr 10, 2022 at 9:22

5 Answers 5

5

In reviewing the magit project and its code, clearly the magit project opinion is to not set global configuration of the git directory, and with clear reasons. (#2939, #3516)

That said, this is your Emacs, and RMS et al want you to hack it. If you are running an Emacs session exclusively for the purpose of editing a repository with a nonstandard git directory, then magit's magit-git-global-arguments configuration provides a suitable hook.

;;; Configure magit to use .myconf (and not .git) as the git
;;; directory when a .myconf directory is found in the current
;;; working directory (which Emacs calls its `default-directory'
;;; per buffer) and there is no .git directory.
;;;
;;; For example, if dotfiles are tracked with a git directory at
;;; $HOME/.myconf, and the Emacs process starts from $HOME (and
;;; nothing in its configuration changes its `default-directory'
;;; in the current buffer), this will configure magit to use
;;; .myconf as the git directory.
;;;
;;; NOTE: This setting will apply for the entire Emacs process,
;;; regardless of magit invocation in other directories.
(unless (boundp 'myconf-magit-hook?)
  ;; Only run this hook once, even if Emacs reloads configuration.
  (eval-after-load 'magit
    '(let ((myconf-path (expand-file-name ".myconf")))
       (when (and (file-exists-p myconf-path)
                  (not (file-exists-p ".git")))
         ;; Insert git directory and working tree into magit's git
         ;; global arguments, while preserving magit's existing
         ;; command-line settings; `add-to-list' adds to the
         ;; beginning of the list.
         (add-to-list 'magit-git-global-arguments
                      (format "--work-tree=%s"
                              ;; Drop trailing slash.
                              (directory-file-name
                               ;; Get directory part (`dirname`).
                               (file-name-directory myconf-path))))
         (add-to-list 'magit-git-global-arguments
                      (format "--git-dir=%s" myconf-path)))))
  (setq myconf-magit-hook? t))

I tested this on GNU Emacs 26.1 with Magit 2.90.1, and I made it a point to avoid any Emacs Lisp features added in the past few years.

It would be convenient to configure magit to look for a nonstandard .git directory in the current buffer's default-directory, but short of monkeypatching (or a fork / pull request), I don't know of a way to configure that (given how global variables are used). Note that magit's design is intended to support multiple repositories within the same Emacs process, and has first-class interactions for a list of repositories.

I have been tracking $HOME files with git and a nonstandard git directory for 9 years. The use case: It's extremely common to run git in a subdirectory of $HOME and a nonstandard git directory for dotfiles will ensure that git never accidentally runs against the $HOME repository when there's no intermediate .git directory when git walks up the directory tree. It's clear from the OP's alias command that this is exactly the use case.

Personally, I run an Emacs session per project.

4
  • Of course, @tarsius (with another answer here) is magit's maintainer, so I'll defer to him. The proposed use of magit-git-global-arguments matches the documented intent of the variable, while preserving its defaults.
    – rduplain
    Commented Jan 5, 2019 at 10:23
  • An alternative approach would be an alias (config-emacs or similar to match the OP alias) to load emacs with an environment variable set, and use (getenv ...) in your Emacs init to determine whether to add the magit hook. It's up to you whether you want to be explicit in starting (via alias) an Emacs process just to edit dotfiles, or to have your Emacs figure it out based on your working directory.
    – rduplain
    Commented Jan 6, 2019 at 0:05
  • I do run a global Emacs handling many repos/workdirs -- one emacs session per project is not viable for me. I tried making it buffer-local but that doesn't work (except for the initial setup) since of course sub-commands run afterwards are not run in the context of the visible buffer. Commented Oct 25, 2019 at 0:46
  • For what it's worth, I'm able to move between standard/nonstandard projects within the same Emacs session using the config that I've listed here. While magit-git-global-arguments is set for the entire Emacs session, the magit buffers initialize correctly and continue to function. I say "for what it's worth" because this is undocumented behavior and therefore could break without warning.
    – rduplain
    Commented Nov 15, 2019 at 10:11
4

I use slightly-modifed @rduplain's approach with tweaking magit-git-global-arguments. This allows for working with standard and non-standard directories at different times within the same Emacs session (not concurrently), at the small price of having a separate shortcut for launching Magit with non-standard directory configured.

;; prepare the arguments
(setq dotfiles-git-dir (concat "--git-dir=" (expand-file-name "~/.dotfiles")))
(setq dotfiles-work-tree (concat "--work-tree=" (expand-file-name "~")))

;; function to start magit on dotfiles
(defun dotfiles-magit-status ()
  (interactive)
  (add-to-list 'magit-git-global-arguments dotfiles-git-dir)
  (add-to-list 'magit-git-global-arguments dotfiles-work-tree)
  (call-interactively 'magit-status))
(global-set-key (kbd "F5 d") 'dotfiles-magit-status)

;; wrapper to remove additional args before starting magit
(defun magit-status-with-removed-dotfiles-args ()
  (interactive)
  (setq magit-git-global-arguments (remove dotfiles-git-dir magit-git-global-arguments))
  (setq magit-git-global-arguments (remove dotfiles-work-tree magit-git-global-arguments))
  (call-interactively 'magit-status))
;; redirect global magit hotkey to our wrapper
(global-set-key (kbd "C-x g") 'magit-status-with-removed-dotfiles-args)
(define-key magit-file-mode-map (kbd "C-x g") 'magit-status-with-removed-dotfiles-args)
3

This won't work. I recommend you set it up like this instead:

cd $HOME
echo "gitdir: ./.myconf" > .git
cd .myconf                     # conceptually, not really necessary
git config --unset core.bare
git config core.worktree ../

At that point git and thus magit know that $HOME is a working tree and where the data is.

Then again, it seems that's what you are trying to prevent (at least for git, why not for magit also?). Because otherwise what is the benefit of storing the git data in $HOME/.myconf instead of in $HOME/.git?

If that is so, then I have another question: what's the benefit of that? I mean I see the appeal of vcsh (which also won't work with Magit), it allows you to use different repositories for the configuration files of multiple packages even though they are all in the same place. But from what you have said, it doesn't appear that this is what you are after.

So it seems the answer is "you can't". In theory you could advice a bunch of Magit functions, and that might kind of work - but there would be bugs, and you would be solely responsible for dealing with them. I would strongly recommend against attempting this.

3
  • too bad this won't work. I will have to think about whether the non-standard .myconf name is really necessary.
    – nbren12
    Commented Feb 10, 2017 at 17:59
  • The benefit is that you can use multiple repositories to make a unified $HOME of config files. See manpages.debian.org/jessie/vcsh/vcsh.1.en.html
    – p_wiersig
    Commented Feb 11, 2017 at 14:08
  • 1
    @p_wiersig As I have mentioned in my answer, I don't think op is actually interested in doing that. However if you do want to do that, then I would still recommend the ".git is a file, set core.worktree approach". Because this has come up before I am even providing a proof-of-concept implementation: github.com/magit/magit/issues/460#issuecomment-36139308.
    – tarsius
    Commented Feb 11, 2017 at 14:16
0

I found that this was sufficient to make git and magit work on the home directory.

cd $HOME
echo "gitdir: ./.dotfiles.git" > .git

Then when I'm done using magit to edit the dot files:

rm $HOME/.git

What's the benefit of a nonstandard git directory for the dot files? @tarsius asked. If you use a shell prompt that shows the current git branch you're on, then your entire home directory will look like it's in some git repo and it is in a way. To avoid that confusion of it looking like git is everywhere, it's nice to special case the dot files so that you're not in a git repository everywhere you turn. I usually couple this with an alias to operate on the dot files:

alias homegit="git --work-tree=$HOME --git-dir=$HOME/.dotfiles.git"

But my command line git fu has atrophied from intense magit usage (thank you, @tarsius!) so it's helpful to be able to tend to my dot files with magit.

1
  • This doesn't let you do anything with magit. View the log, but you already could that by opening .dotfiles.git in magit without creating the .git directory
    – Sandra
    Commented Feb 29 at 8:00
0

Combining Shane's idea to temporarily change the git setup with tarsius' idea to change the worktree option in that setup, I made a script for this:

#!/bin/sh
echo "gitdir: $(realpath "$1")" > ~/.git
cd "$(realpath "$1")"
git config --unset core.bare
git config core.worktree ../
echo "Do your magit stuff! Then hit RET here when you're done."
read nothing
git config --unset core.worktree
git config core.bare true
rm ~/.git
cd -

Run the script with ~/.myconf as the argument or whatever you're calling your git directory for this, Shane uses .dotfiles.git, I use .cfg.

Using echo and not cat (?) and also remembering to unset core.bare since git gets confused when it and worktree are both set.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.