Is this git-only behavior?
After discussion with a colleague, I just tried, and SVN handles it without problem: you get the 2 lines modified.
The merge capabilities of several VCS are tested here for bazaar, darcs, git and mercurial: https://github.com/mndrix/merge-this
It seems only darcs successfully merge the "adjacent lines" case.
Applying adjacent changes to files is not a difficult problem.
I really think this behavior has been chosen on-purpose.
Why would someone decide that modifying adjacent lines produces a conflict?
I would think this is to force you to look at it.
int max = MAX_ITEMS;
for(unsigned int i = 0; i < max; i++)
do_stuff(i);
Modif number 1, on master:
int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max; i++)
do_stuff(i);
Modif number 2, merged from a branch:
int max = MAX_ITEMS;
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
do_stuff(i);
After merge, you don't want that:
int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
do_stuff(i);
Seeing this behavior as a feature
You can turn the git merging behavior to an advantage.
When you need to keep 2 lines consistent but you can't detect it (at compilation time, early in your tests or else), you can try to join them.
Rewrite this...:
for(unsigned int i = 0; i < max; i++)
r = do_stuff(i);
// Need to do something else
do_something_else(r);
...to this:
for(unsigned int i = 0; i < max; i++)
r = do_stuff(i);
do_something_else(r); // Need to do something else
So when you merge Modif 1...:
for(unsigned int i = 0; i < max; i++)
r = do_stuff(i)/2; // we need only the half
do_something_else(r); // Need to do something else
... with Modif 2...:
for(unsigned int i = 0; i < max; i++)
r = do_stuff(i);
if(r < 0) // do_stuff can return an error
handle_error(r);
do_something_else(r/2); // Need to do something else
..., git will produce a conflict, and you will force you to look at it.
Conflicts occur because two people change the same part of the code in different, incompatible ways. If you see a lot of them, this should raise some questions about your basic development methodology.
First, you appear to be abusing branching rather severely. Giving every developer their own repository basically defeats the purpose of having a collaborative version control system in the first place. (Yes, you still have the advantages of keeping a history and a "time machine" that you can roll back to, but that's only half the point of a VCS when you have a development team.)
Your project ought to be branched by version--a 3.0 branch, a 3.1 branch, a 3.2 branch, and so on--not by developer. When you need a stable revision of a version for a release, turn it into a Tag.
Having your developers all check in to the same branch (and synchronize frequently) minimizes the number of merge conflicts you run into, because having a shorter interval between merges means that it's less likely that two developers will touch the same code without synchronizing.
Also, try to assign specific areas of the codebase to specific developers. Again, this cuts down on the number of times their changes will overlap, which cuts down on merge conflicts.
Best Answer
What concerns me here is that you make it sound like anyone checking in code has to blindly check it in to multiple branches. That is wrong. Branches should exist for a reason. The event of checking in code should happen once and flow from there. Who gets it flowing isn't as important as that branches are kept meaningful and useful in each step.
It sounds to me like you had a better approach in the past. The question then is: why did that change?