Committing Machine Specific Configuration Files
Solution 1:
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.
Solution 2:
You can try git update-index --skip-worktree filename
. This will tell git to pretend that local changes to filename don't exist, so git commit -a
will ignore it. It has the added advantage of also resisting git reset --hard
, so you won't accidentally lose your local changes. Also, automatic merges will fail gracefully if the file is changed upstream (unless the working directory copy matches the index copy, in which case it will be automatically updated). The downside is the command has to be run on all machines involved, and it's difficult to do this automatically. See also git update-index --assume-unchanged
for a subtly different version of this idea. Details on both can be found with git help update-index
.
Solution 3:
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.