Why is it bad idea to create external configuration file for your app

It is tempting vision. You are creating single configuration file in each server box. Each developer can create this file also on local box. Your application during runtime will look inside the config file located in home directory and you can manage different variables for each machine. It should work, right? Usually yes but you have to remember:
  • backup - configuration is very critical part of application, in some cases it's extremely hard to get all those variables (mostly credentials) of your config from the sky,
  • share - provide this file for every new dev, new box in project,
  • edit permissions - login to server and manually edit configuration variables along with every new/existing feature that requires new key,
  • comments - provide comments what each line of file is doing,
  • cloud - lot of cloud providers are not giving you option to store files in different directories.

There is, however different way of doing this. You can/should store configuration along with your source code, control it for example by environment variable. How this will affect above cases?
  • backup - source code repository does this for you,
  • share - new dev can checkout this file, use config similar to file that is used by different developer, depends on repo structure, can commit also his own overrides
  • edit permissions - every developer can edit this, just edit configuration variables along with every new/existing feature that requires new key,
  • comments - comments are still good here, however thanks to source code repository you can also view commit comments and history of file,
  • cloud deployments - usually it's easy to setup environment variable.
In summary, you can use external configuration in project. Just make sure that you will be happy with new responsibilities.


Git on top of svn repository - perfect harmony?

Svn + Git branching Introduction

Recently I've been working in a team which used corporate Subversion repository and from time to time - during preparation for new version release - we were not permitted to commit any new changes to the repository except of bug fixes for current release. It happened once that this "code freeze" period took over 2 weeks and you can image what happened when a team of 15 devs stashed their svn commits locally for 2 weeks waiting for "green light" and how many conflicts there were to be resolved after it happened.

Using subversion was already a painful experience for me after months of using Git in other projects so I wanted to try building my own local repository in Git on top of svn. Searching for a reliable git proxy mechanism failed because in our project we're using SVN externals, which git couldn't handle. The only idea left was to simply place git repository in the same location where svn lies.

Why Git is better than Subversion?

Although it’s only my personal opinion which you may disagree with, yet advantages of git are undoubtedly big. In the situation described above (similar to the one where you have to work offline from time to time) you can work on couple of different tasks in separated “environments”  - git branches - simultaneously or one after another, it doesn’t matter. You don’t have to mix all your changes in one repository, and then wonder “whether this line was changed for fixing TASK_A or TASK_B?”, “should I commit it here or there?”. You can easily switch context, make your changes (unrelated to each other) starting every time in the clear repository. It also happens very often that you start working on something at work and then you may want to continue your work at home on your private computer. Using svn, you would have to make a commit with code under development (with risk of breaking the app) and then continue at home, or maybe even transfer your changes via email/ubs stick/whatever, that doesn’t seem to be a good idea. In git, you can create your own “working” branch, commit whatever you like, finish your work at home and merge it to master branch. No one will notice, no build gets broken, history of changes is preserved, everyone is happy :)

How to?

Assuming you already have installed Git in your system, basically all you need to do is

$ git init

in the root of your svn working copy directory. Next you may want to ignore each repos from one another, so that git doesn’t checks in .svn directories and svn ignores .git. In git, it’s simple, you have to create .gitignore file in the root of your local repository and tell it to ignore .svn objects, you can also commit this file to your git repo.

$ echo ".svn" > .gitignore
$ git add .gitignore
$ git commit -m "added gitignore"

You should do the same from Subversion side - I won’t give you more details because it depends on what tool you’re using as your svn frontend (tortoise svn, eclipse, intelliJ, svn cli) but basically you should add to svn:ignore all .git directories and .gitignore files. If you did everything properly, your svn shouldn’t have any pending outgoing changes.

Then, you should also consider .gitignoring other files, depends on your project. Finally, you should do the first “real” commit in order to save your repository in initial state.

$ git add .
$ git commit -m "initial commit"

Dealing with line endings

I’ve been a huge fan of git since the day I started using it (well… alright, two weeks later :D), it’s way better than svn in every aspect. There is only one thing that could be considered troublesome - CRLF’s, end of line characters. By default, git converts line endings to your-OS-specific on checking-in and converts it back on checking-out. As long as stick with it and do everything as you should, everything will be fine. However, it may happen that for some reason you move your dev environment from Linux to Windows (e.g. switching to new workstation) and you recklessly copy your workspace “as is” from one system to another - god forbid, you will end up fixing your line-endings for sure.

The thing I’m describing here is somehow also vulnerable to such problems. After all, you will probably not push/pull things using git. It is intended to be only your local repository. Also, you will not clone the repository in the first place, you just init your repo over existing files so your repository has to accept line ending as they are and not modify it. As I observed, in default configuration, git also modifies line endings on switching branches, and that’s what you will definitely want to use.

In order to avoid these problems, you should configure your git repository so that it doesn’t modify any line endings at any time.

$ echo "* -crlf" > .gitattributes
$ git add .gitattributes
$ git commit -m "added gitattributes"
$ git config core.autocrlf false

After executing git status you should also see no unstaged nor uncommitted changes left. And basically, that’s it. You have configured your git repository on top svn working copy.

(reference: github's line endings guidelines)

Branch per task approach

It is highly advisable to use local git repository in a way where every logically separated portion of changes is being made in a separate git branch (e.g. related with one task). Unlike svn, git branches can be easily switched between. Subversion creates copy of whole repository in order to create branch while git just rewinds and apply diffs on the same files. Thanks to that, you don't have to create separate workspaces in your IDE per branch, but you just switch them in-place.

Real-life example

Let's consider an example scenario how you could use the described approach.

Initial state, then you are assigned BUG_A to fix

git checkout -b bug_a
Branch "bug_a" has been created

git commit -m "fixed bug a"
You have fixed bug a and committed your changes

git checkout master
You switch your local repo branch master so it's back in the initial state In your svn client you do an update

git status
Incoming changes are downloaded and git treats them as your changes to be committed

git commit -m "svn update #1"
You do a commit in master branch so that you have a clear local repo again and so that you could rebase your branches against it You realize that you still have to do something related with bug_a

git checkout bug_a
You have switched back to bug_a branch with your fix, but without the recently downloaded incoming changes

git rebase master
Incoming changes committed in master branch are being applied on your branched repository - first your "fixed bug a" commit gets rewinded, then all incomming changes are applied and then your commit is applied on top. Now you have up-to-date code and your fix applied, you can continue your work.

git commit -m "fixed one more thing related with bug_a"
git checkout master
Committing your further changes and switching back to master branch. Meanwhile, BUG_B has been assigned to you for fix.

git checkout -b bug_b
You created another branch - bug_b - this time it already contains all incoming changes, because you branch out from the master branch.

git commit -m "fixed bug b"
git checkout master
You have fixed bug_b, committed your changes in separate branch and switched back to master. At the moment, you have fixed two issues in separate branches, while your master branch is still the same as the last revision updated from svn, so no conflicts can occur on following updates if only you do them in clean master branch.

"Code freeze" phase ended and now you want to check in your fixes to svn.

git checkout master
Switch back to master branch (if you were somewhere else) and do svn update again.

git add .
git commit -m "svn update #2"
Commit incoming svn changes to git master branch

git merge bug_a
Apply changes related with bug_a on your master branch. Now you can commit your fix for BUG_A to SVN.

git merge bug_b
Do the same with bug_b. And commit again to SVN.

In the end, you have committed your changes for 2 bugs in separate commits. You've greatly reduced the risk of having conflicts on svn updates, you didn't have to worry about remembering which files should you commit together. If only you are quite confident with using git, there is no risk of breaking anything.