A common scenario when I develop is that the codebase will have several config files which require machine specific settings. These files will be checked into Git and other
Another approach is to maintain local changes to common configuration files in another private branch. I do this for some projects that require several local changes. This technique may not be applicable to all situations, but it works for me in some cases.
First I create a new branch based on the master branch (in this particular case I'm using git-svn so I need to commit from master but that's not terribly important here):
git checkout -b work master
Now modify the configuration file(s) as necessary and commit. I usually put something distinctive in the commit message like "NOCOMMIT" or "PRIVATE" (this will be useful later). At this point, you can work away on your private branch using your own config file.
When you want to push your work back upstream, cherry-pick each change from your work
branch to the master. I have a script to help do this, which looks something like this:
#!/bin/sh
BRANCH=`git branch | grep ^\\* | cut -d' ' -f2`
if [ $BRANCH != "master" ]; then
echo "$0: Current branch is not master"
exit 1
fi
git log --pretty=oneline work...master | grep -v NOCOMMIT: | cut -d' ' -f1 | tac | xargs -l git cherry-pick
This first checks to make sure I'm on the master
branch (sanity check). Then, it lists each commit in work
, filters out the ones that mention the NOCOMMIT keyword, reverses the order, and finally cherry-picks each commit (now from the oldest first) into master
.
Finally, after pushing the changes in master upstream, I switch back to work
and rebase:
git checkout work
git rebase master
Git will reapply each of the commits in the work
branch, effectively skipping over the one(s) that have already been applied in master
through the cherry-picking. What you should be left with is only the NOCOMMIT local commits.
This technique makes the push process a bit more time-consuming, but it solved a problem for me so I thought I'd share.
Nowadays (2019) I use ENV vars for example in python/django, you can also add defaults to them. In the context of docker I can save the ENV vars in a docker-compose.yml file or an extra file which is ignored in version control.
# settings.py
import os
DEBUG = os.getenv('DJANGO_DEBUG') == 'True'
EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST', 'localhost')
The simplest solution is to edit the file to defaults, commit it, then add it to your .gitignore
. This way, developers will not accidentally commit it when doing git commit -a
, but they can still commit it in the (presumably rare) case where you want to change your defaults with git add --force
.
However, having a .default
and .local
config file is ultimately the best solution, since this allows anyone with a machine-specific configuration to change the defaults, without having to break their own configuration.
Check in a default configuration with a different extension (say .default), use a symlink to symlink the default to the correct location, add the correct location to .gitignore, and add everything else related to the configuration to .gitignore (so the only thing that gets checked in is config.default).
Additionally, write a quick install script that sets up the symlinks for your application-wide.
We used a similar approach at a previous company. The install script autodetected what environment you were running in (sandbox, development, QA, production), and would automatically do the right thing. If you had a config.sandbox file, and were running from the sandbox, it would link that (otherwise it would just link the .defaults file). Common procedure was to copy .defaults and change settings as necessary.
Writing the install script is easier than you might imagine, and gives you a lot of flexibility.