Evan Moses

My blog and projects

Separating work and personal config

TL;DR

  • My dotfiles are checked into a git repo
  • I want to avoid checking in sensitive, work-specific config to a public git repo
  • I updated my git and emacs configs to check for local overrides in ~/.local

Like many folks, I check my dotfiles into a git repo to share them across multiple machines. When I set up a new machine I can simply clone that repo, run an install script that adds some symlinks, and get to work with my happy personalized config.

My work machine needs to have some special configuration. I’m required to use certain security tools, there are some dev tools I only use at work, plus obviously I need to use my work GitHub account. The security team at my company has (wisely) asked me to keep the work-specific configuration out of my public GitHub, in order to prevent attackers from performing reconnaissance on our internal tooling and network configuration. Here’s how I managed that.

Git config

Our internal git setup uses a credential helper, a tool I’ve written about at Building a custom Emacs auth-source . In order to use it, we need this in our gitconfig:

...
[core]
	sshCommand = /usr/local/bin/awesome-security-tool ssh
...
[credential]
	helper = /usr/local/bin/awesome-security-tool helper

I added this line near the bottom of my dotfiles/git.config (which is symlinked to ~/.gitconfig). Git applies configuration in order from top to bottom, with later values overwriting earlier values, so if this were at the top, it would be overwritten by the “global” configs from your base dotfiles.

[include]
    path = ~/.local/git.config

Then I moved the work-specific config to ~/.local/git.config. Git is happy to silently ignore the include if there’s nothing at that path, so I can safely sync this config down to any machine even if it doesn’t need any local configuration.

Per-repo overrides

On the other hand, when I’m working on non-work repos on my work machine, I want to use my personal configuration instead. I also want to make sure I’m not using the fancy credential helper, because the credentials it gets won’t work with my personal account.

This can be configured on a per-repo basis in the .git/config for each repo (or by using git config without the --global flag), but git has a neat feature that will conditionally include a config file based on the path to the repo. I check out my personal repos to a particular directory, so I at this config at the bottom of my gitconfig1, after the include from earlier:

[includeIf "gitdir:~/dev/github.com/emoses/"]
   path = ~/.gitconfig.personal

Where .gitconfig.personal looks like

[user]
        email = webmaster@emoses.org
[core]
        # Override the ssh command from the work-specific config back to standard
        sshCommand = /usr/bin/ssh

Emacs configs

My emacs also has some work-specific customizations, which I keep in a file called work.el that used to be checked in to my dotfiles repo. My routine for loading all my customizations in my .emacs looks like this (if you’re really curious you can find the rest here) :

(defvar my:osx (eq system-type 'darwin))

(my:load-config-file '("package-bootstrap.el"
		       (lambda () (if my:osx "osx.el" nil))
               "evil.el"
                ;; A bunch more files
                "work.el" ;; Whoops, this was checked in, let's fix that
                ))

I added a routine to load any configs from ~/.local/emacs if they’re present.

(defconst my:LOCAL_CONFIG_PATH (file-name-concat (getenv "HOME") ".local" "emacs"))
(when (file-exists-p my:LOCAL_CONFIG_PATH)
    (let ((local-el-files (directory-files my:LOCAL_CONFIG_PATH t "\.elc?$")))
      (dolist (local-el local-el-files)
        (load local-el)
        (message "Loaded local config file: %s" local-el))))

So I moved my work.el out of my dotfiles repo to ~/.local/emacs/work.el and now I’m all set.


  1. When you use include or includeIf, git treats it as if the contents of the included file were at the point where your include directive is. Since later configs override earlier configs with the same name, if you want per-repo overrides you’ll need them to be at the bottom or they’ll simply be overwritten by the “default” configs. More info in the Git docs ↩︎