Resolving composer merge conflicts
March 27, 2013Marek Kalnik3 min read
One of the difficulties when working with composer is how to merge its files.
When you are working on multiple branches with your team members and two of you
update the project, either to add a new package or to update one that is already
used by the project, you end up having a nasty merge conflict.
One solution for this would be a global composer update but this would result
in updating all bundles and some changes that are not tested could introduce bugs.
Even an update of only concerned bundles does not assure that you will end up with
the same bundle version that your colleague has tested.
So, what good practices could you adopt to make sure you are safe after a merge of
Keep your composer up to date
Composer is still evolving and so are the files it uses. It may be one of the sources of conflicts - even if there were no major changes in the composer.json, a different composer version may generate a completely different composer.lock. To avoid such kind of merge errors, keep your composer up to date and use the same version in the team. If you use a composer.phar versionned in the project you can put someone in charge of updating it. If you prefer using local copies per user you can fix a day of the week when everyone updates it or have someone mail the correct version to the whole team.
Keep dependency changes visible
Anyone who is merging another branch should be able to say what has changed and when. To make this easy follow those simple rules:
1. Keep your commits atomic. If you are adding a dependency, make one addition at a time. Remember to keep your commit functional (in case someone needed to debug with git bisect) so think about updating your AppKernel and config files. If you are updating a dependency, composer lets you update packages separately:
composer update vendor/package
2. Keep a clear and consistent commit messages. Use a tag (like [deps]) and a good description. Use rather "[deps] Update CoolBundle to 1.3" or "[deps] Add CoolerBundle 1.0.3" than "updated deps".
Do not use dependencies directly
Everytime you use a dependency of another project, add it to your composer.json file. This will help you to make sure that none of your functionalities is broken by an indirect update.
Try to never rely on dev-master
Whether you are a package owner or developing a project that depends on some package, try to avoid relying on dev-master. It is just plain wrong, that your project will work with the latest version all the time so it has no informational value. Also, if one package depends on a tagged version and another depends on a dev-master version it is just hell to maintain.
If you want to use a package in dev-master version - contact the maintainer and ask him if he could add a branch alias so you would use it rather than the dev-master.
If you see a package that relies on dev-master branch of another package and it can be replaced by a branch/tag - think about submitting a pull-request that fixes the issue. The few seconds you spend on this may save you a lot of headaches later.
Resolve conflicts consistently
Here is how you can resolve a composer conflict trying to keep most of the versions you are used to.
1. Use git to start the merge from your version of the file:
$ git checkout --ours composer.lock
2. See what has changed in composer.json:
$ git diff HEAD MERGE_HEAD -- composer.json
3. If a dependency has been removed you can safely remove it with composer:
$ composer update dependency-package
If there are no packages that were updated alongside you can safely add both composer files and finish the merge.
4. For each updated package, modify your composer.json if a tag was updated. If not, you can use the commit hash notation to download the correct version.
Remember to remove the commit tag once you have run the composer update command.
5. For every package that was added you need to user composer update package. This may change other dependencies but, unless you want to keep track of all of them
(which is not a great idea), it is your only option.
This is how I try to resolve a complicated composer merge when I face one. And how do you do it?