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 developers will always accidentally check them back in and break someone else's configuration.
A simple solution to this would be to just not check them in to Git, or even to additionally add a .gitignore entry for them. However, I find that it is much more elegant to have some sensible defaults in the file which the developer can modify to suit his needs.
Is there an elegant way to make Git play nicely with such files? I would like to be able to modify a machine-specific configuration file and then be able to run "git commit -a" without checking that file in.
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):
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:This first checks to make sure I'm on the
master
branch (sanity check). Then, it lists each commit inwork
, filters out the ones that mention the NOCOMMIT keyword, reverses the order, and finally cherry-picks each commit (now from the oldest first) intomaster
.Finally, after pushing the changes in master upstream, I switch back to
work
and rebase:Git will reapply each of the commits in the
work
branch, effectively skipping over the one(s) that have already been applied inmaster
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.
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 aconfig.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 likeinclude 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 toinclude conf.d
, which reads configuration from all the files in a directory.Also see my answer to a similar question.
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.
I agree with the best answer but also would like add something. I use an ANT script to strip & modify files from the GIT repo so I'm sure no production files get overwritten. There is a nice option in ANT to modify java-property files. This means putting your local test variables in a java-style property file and adding some code to process it, but it gives you the opportunity to automate building your site before you FTP it online. Typically you would put your production information in the site.default.properties file, and let ANT manage the settings. Your local settings would be in the site.local.properties.
then use it:
Your site.default.properties would look like:
and your site.local.properties would look like (notice the difference environment and enabled modules):
And your ANT instructions: ($d{deploy} being your deployment target directory)
Building on @Greg Hewgill's answer, you could add a specific commit with your local changes and tag it as localchange:
Then proceed to add your feature's commits. After finishing the work, you can merge this branch back to master without the localchange commit by doing this:
These commands will:
1) Rebase your feature branch to master, ignoring the localchange commit. 2) Fast forward master without leaving feature branch 3) Add localchange commit back to the top of the feature branch so you can continue working on it. You can do this to any other branch you want to continue working on. 4) Reset localchange tag to this cherry-picked commit so we can use
rebase --onto
again in the same way.This isn't meant to replace the accepted answer as the best general solution, but as a way of thinking out of the box about the problem. You basically avoid accidentally merging local changes to master by only rebasing from
localchange
tofeature
and fast forwarding master.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 doinggit commit -a
, but they can still commit it in the (presumably rare) case where you want to change your defaults withgit 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.