Composer & composer.lock in GIT and merge conflicts

Another approach (without doing composer update):

  1. Copy your new (and deleted) lines from composer.json into a separate text file.
  2. Use entire remote composer.json and composer.lock files.
  3. During merge conflict mode do:
    • composer install
    • For every new package your wrote down in step 1 run composer require vendor/package:version
    • For every removed package your wrote down in step 1 run composer remove vendor/package
  4. Testing!, Commiting, done!

This method will keep locks from remote branch (maybe master or develop branches), and only updates your new packages.


How can you test that a core update (or any other dependency that gets updated) doesn't break things in the projects using it if the developer don't do this step themselves?

That's why the usual workflow is expecting the composer update being run on a development machine having enough RAM (i.e. probably more than 1GB set as memory limit for PHP), and the update should be triggered manually by the developer (and if triggered automatically by a continuous integration build, the memory requirements apply to this machine as well).

There is no way around this memory requirement. A web server with only 512 MB RAM installed might be able to function as a staging server with barely any concurrent users present, but it shouldn't be used to update the Composer dependencies.

Personally I fix the merge conflicts in the composer.lock with a very easy system: Delete the lock file and run composer update. This will update all dependencies to the latest versions that satisfy the version requirements, and create a new working composer.lock file that get's committed during the merge.

I am not afraid to potentially update everything, because either it works as expected, or my tests will catch errors quickly.

I do select the 3rd party packages I use carefully:

  • they have to tag their versions, preferably using semantic versioning.
  • I do not use any branches for release versions (the rare occasions that someone used them during development were painful)
  • they should ship a new major version if they make backwards incompatible changes
  • the locally developed packages also follow these requirements

This works with around 270 packages served by our local Satis instance (probably also a factor to consider when trying to reduce memory footprint - only the packages known to Composer can end up in memory: Compare the ten thousand packages potentially available on packagist.org with 270 local packages). 60 packages of the 270 are locally developed by 20 developers, and randomly releasing new versions. The update failures in the last 2 years are very rare, and should be handled like other bugs: If a tagged version is detected to be incompatible, we release a bugfix release reverting the change, and tag the original change with a new major release, if the incompatible change is necessary.

So the workflow you ask for is probably like this:

  • Anytime, any developer should be able to run composer update on their local machine.
  • They should be able to detect if this breaks things on their local machine.
  • If nothing is broken, they commit the changes including the composer.lock file to Git
  • The staging server only runs composer install and will use exactly the versions that the developer used on his machine.
  • If nothing is broken on staging, that version is ready to be used on production.

Merging an already committed version on another developers machine will likely show merge conflicts with composer.lock.

  • Resolve conflicts on all other files.
  • The composer.lock file should be deleted.
  • From here, the workflow is like above, i.e.:
  • The developer should be able to run composer update on his local machine.
  • They should be able to detect if this breaks things on his local machine.
  • If nothing is broken... and so on.

Sometime composer update can break things. What I do is.

  1. Discard all of my changes on composer.lock
  2. Merge composer.json
  3. Run composer install so that packages get installed according to composer.lock.
  4. Take one of your package and run composer require vendor/package if you added that package or composer remove vendor/package if you removed it. if there are multiple packages it will be installed automatically from composer.json