Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using conditional configuration files with Git

Tags:

git

config

I frequently find myself changing a single variable in my project within Git to connect to a different server while on the Development branch (in JavaScript, so I can't use preprocessor defines).

Is there some way in Git that I can conditionally use one file or another depending on what branch I'm on?

I can't just commit that change with the different URL specified as doing so would leave it in my commit history, and if I go back to that version later on Master after it has been merged, it will have the development server URL.

like image 750
Christian Stewart Avatar asked Jun 30 '14 20:06

Christian Stewart


2 Answers

No, there isn't, but this is a well-solved problem.

You have a few options:

Version control an example config file

  • Don't store environment-specific data in version control
  • Create a config.example file which lists all the configuration options that need to be specified, and provides sane defaults for development.
  • Users who clone your repo should copy config.example to the real config filename, and add real values
  • Add the real config file's name to .gitignore.
  • Store your production credentials outside of git, but backed up,
  • As a bonus, you can add a setup.sh script, which copies config.example to the real config's location, and populates it with variables for the local environment

As an example, you might have a JavaScript application which needs to know where its database is, and reads this information from config/database.json. You might use something like this:

// config/database.example.json
DATABASE = {
  "host": "localhost",
  "user": "#TODO",
  "pass": "#TODO",
}

To get running in development, you would copy this file to config/database.json, and fill in the values appropriate to your dev environment.

In production, you'd have a config/database.json that contained production values, but was not version controlled.

The repo would have config/database.json in its .gitignore.

Version control environment-specific config files

  • Store X different configuration files, one per environment, call them config.development and config.production etc
  • Symlink the correct one for your environment.
  • add the symlink to .gitignore

If there is anything remotely sensitive in your config file, such as AWS keys or any form of password, you should use the first option - store the configuration option's name, but not its value, and require users to supply their own credentials, obtained through secure channels outside of version control.

like image 150
meagar Avatar answered Oct 30 '22 00:10

meagar


Note that condition include config is coming, starting with Git 2.13 (Q2 2017).
The only condition supported as of now is the project name, not the hostname.

In your case, that would work if each branch is checked out in its own folder (with git worktree: see "Multiple working directories with Git").

See commit 86f9515, commit 4aad2f1 (05 Apr 2017) by Nguyễn Thái Ngọc Duy (pclouds).
(Merged by Junio C Hamano -- gitster -- in commit a2e2c04, 24 Apr 2017)

config: add conditional include

Sometimes a set of repositories want to share configuration settings among themselves that are distinct from other such sets of repositories.
A user may work on two projects, each of which have multiple repositories, and use one user.email for one project while using another for the other.

Setting $GIT_DIR/.config works, but if the penalty of forgetting to update $GIT_DIR/.config is high (especially when you end up cloning often), it may not be the best way to go.
Having the settings in ~/.gitconfig, which would work for just one set of repositories, would not well in such a situation.
Having separate ${HOME}s may add more problems than it solves.

Extend the include.path mechanism that lets a config file include another config file, so that the inclusion can be done only when some conditions hold.
Then ~/.gitconfig can say "include config-project-A only when working on project-A" for each project A the user works on.

In this patch, the only supported grouping is based on $GIT_DIR (in absolute path), so you would need to group repositories by directory, or something like that to take advantage of it.

We already have include.path for unconditional includes.
This patch goes with includeIf.<condition>.path to make it clearer that a condition is required.
The new config has the same backward compatibility approach as include.path: older git versions that don't understand includeIf will simply ignore them.


Git 2.14 (Q3 2017) clarifies the documentation.

See commit ce933eb, commit a076df2, commit 994cd6c, commit 9d71d94 (11 May 2017) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit ed98060, 29 May 2017)

The documentation now reads and includes:

Includes

The include and includeIf sections allow you to include config directives from another source. These sections behave identically to each other with the exception that includeIf sections may be ignored if their condition does not evaluate to true; see "Conditional includes" below.

The same Git 2.14 reinforce that feature.
See commit 0624c63 (16 May 2017) by Ævar Arnfjörð Bjarmason (avar).
(Merged by Junio C Hamano -- gitster -- in commit b784d0b, 30 May 2017)

The recently introduced "[includeIf "gitdir:$dir"] path=..." mechanism has further been taught to take symlinks into account.

The directory "$dir" specified in "gitdir:$dir" may be a symlink to a real location, not something that $(getcwd) may return.
In such a case, a realpath of "$dir" is compared with the real path of the current repository to determine if the contents from the named path should be included.

like image 42
VonC Avatar answered Oct 29 '22 23:10

VonC