How to Track System-Specific Config Files in a Repo/Project

How can I track system-specific config files in a repo/project?

I would recommend using:

  • a template config file (a file with variable name in place of the host and port value)
  • a script able to replace those variable names with the appropriate values depending on the environment (detected by the script)

The Git solution is then a git attribute filter driver (see also GitPro book).

A filter driver consists of a clean command and a smudge command, either of which can be left unspecified.

Upon checkout, when the smudge command is specified, the command is fed the blob object from its standard input, and its standard output is used to update the worktree file.

Similarly, the clean command is used to convert the contents of worktree file upon check-in.

That way, the script (managed with Git) referenced by the smudge can replace all the variables by environement-specific values, while the clean script will restore its content to an untouched config file.

clean smudge

When you checkout your Git repo on a prod environment, the smudge process will produce a prod-like config file in the resulting working tree.

What's the easiest way to deal with project configuration files?

Filter drivers are the "automatic" way of implementing option 3, as detailed in "when you have secret key in your project, how can pushing to GitHub be possible?":

enter image description here

The smudge script will, on checkout:

  • detect the right config files to modify
  • fetch the information needed (best kept outside any Git repo) and will replace the template values by the actual one.

From there the developers can make any kind of modification they want to those config files.

It won't matter, because the clean script will, on commit, restore the content of that file to its original (template) value. No accidental push there.

How do you deal with configuration files in source control?

What I've done in the past is to have a default config file which is checked in to source control. Then, each developer has their own override config file which is excluded from source control. The app first loads the default, and then if the override file is present, loads that and uses any settings from the override in preference to the default file.

In general, the smaller the override file the better, but it can always contain more settings for a developer with a very non-standard environment.

Handling different configurations with git

Have a branch for each configuration.

For instance, you could have a production branch and a dev branch.

On the production branch, just make one commit to change the configuration file, then use the dev branch the same way you used your master branch.

The deployment process becomes

On dev:

  • git checkout production
  • git merge dev
  • git push origin production

On production:

  • git pull origin production

Or split your configuration file

And maybe add some runtime test to pick the good one at the right time.

Committing Machine Specific Configuration Files

Have your program read a pair of configuration files for its settings. First, it should read a config.defaults file that would be included in the repository. Then, it should read a config.local file that should be listed in .gitignore

With this arrangement, new settings appear in the defaults file and take effect as soon as it's updated. They will only vary on particular systems if they're overridden.

As a variation on this, you could have just a general config file that you ship in version control, and have it do something like include config.local to bring in the machine-specific values. This introduces a more general mechanism (versus policy) in you code, and consequently enables more complicated configurations (if that's desirable for your application). The popular extension from this, seen in many large-scale open-source software, is to include conf.d, which reads configuration from all the files in a directory.

Also see my answer to a similar question.

Git best practice for config files etc

Use symbolic links.

Take an example where you have a config file named "config.ini". In the working directory of your git repo, you would do the following:

  1. Create a version of the config file called "config-sample.ini". This is the file you'll do all your work on.

  2. Create a symbolic link between "config.ini" and "config-sample.ini".

    ln -s config-sample.ini config.ini

    This let's all your code point to "config.ini" even though you're really maintaining "config-sample.ini".

  3. Update your .gitignore to prevent the "config.ini" from being stored. That is, add a "config.ini" line:

    echo "config.ini" >> .gitignore
  4. (Optional, but highly recommended) Create a .gitattributes file with the line "config.ini export-ignore".

    echo "config.ini export-ignore" >> .gitattributes
  5. Do some coding and deploy....

  6. After deploying your code to production, copy the "config-sample.ini" file over to "config.ini". You'll need to make any adjustments necessary to setup for production. You'll only need to do this the first time you deploy and any time you change the structure of your config file.

A few benefits of this:

  • The structure of your config file is maintained in the repo.

  • Reasonable defaults can be maintained for any config options that are the same between dev and production.

  • Your "config-sample.ini" will update whenever you push a new version to production. This makes it a lot easier to spot any changes you need to make in your "config.ini" file.

  • You will never overwrite the production version of "config.ini". (The optional step 4 with the .gitattributes file adds an extra guarantee that you'll never export your "config.ini" file even if you accidentally add it to the repo.)

(This works great for me on Mac and Linux. I'm guessing there is a corresponding solution possible on Windows, but someone else will have to comment on that.)

Git: application configuration and different environments

Not sure why people think they can get away without some sort of install tool. Git is about tracking source, not about deploying. You should still have a "make install"-type tool to go from your git repo to the actual deploy, and this tool might do various things like template expansion, or selection of alternate files.

For example, you might have "config.staging" and "config.production" checked in to git, and when you deploy to staging, the install tool selects "config.staging" to copy to "config". Or you might have a single "config.template" file, which will be templated to make "config" in the deploy.

Is it possible to have different Git configuration for different projects?

The .git/config file in a particular clone of a repository is local to that clone. Any settings placed there will only affect actions for that particular project.

(By default, git config modifies .git/config, not ~/.gitconfig - only with --global does it modify the latter.)

Related Topics

Leave a reply