Efficiently resolving composer.lock git merge conflicts

Ion Bazan
4 min readApr 6, 2021

--

When working in a team on a PHP project using Composer, you have probably encountered a problem when multiple people added, removed or updated some packages in composer.json and composer.lock on main branch and GIT welcomed you with this message in the morning:

Auto-merging composer.lock
CONFLICT (content): Merge conflict in composer.lock

Resolving such conflict is not a straightforward operation due to the structure of the file which contains all locked dependencies information and a content hash.

In this article I will show you few methods to resolve such conflict and make your life a bit easier.

TL;DR

git checkout --theirs composer.lock
composer update php # the "php" is important here
Photo by Sean Lim on Unsplash

What not to do?

I’ve seen people doing git checkout --ours composer.lock and pushing the changes… Since it’s overwriting all others’ changes with yours, it’s basically an equivalent of force-pushing changes to main branch — never do that!

Another bad (but not as destructive) idea would be to remove composer.lock file and recreate it using composer update. This might seem like a good idea if you don’t really care about your locked versions and trust your version constraints and tests 100%. In real life, this will most likely introduce some unexpected changes into your feature branch and unnecessarily increase the diff.

Method 1: The fast & simple [my favorite!]

This method is suitable for most common scenarios, when some packages were added or updated in main branch and you are trying to merge it into your feature branch with some other packages added. Assuming there are no dependency conflicts, you are good to go with this solution after merging the composer.json file.

First, accept the changes from base (main) branch:

git checkout --theirs composer.lock

Then, trigger an update of the packages you added or removed:

composer update php

Please note that you may pass any installed (or even not installed) package name to the update command for it to work but passing php will only touch the packages you just added or removed, without unnecessarily updating any extra packages to keep your changes clean.

Simple and efficient!

Pros:

  • Fast
  • Simple
  • Universal — you can use same commands, no matter what packages you add or remove

Cons:

  • Packages you added on your feature branch will be updated to most recent version matching the constraints (usually it should not be an issue)
  • Any package updates (via composer update) from your feature branch will be gone — you should re-run the update afterwards if needed

Method 2: Redoing your changes

This method is described in the official Composer documentation. In some cases, when there might be some conflicting packages, it might be better to redo you package additions/removals on top of most recent changes from main branch — similarly as applying changes during a rebase.

First, accept the changes from base (main) branch:

git checkout --theirs composer.lock composer.json

Then, add all the packages you added or removed in your feature branch:

composer require package/a:^1.2
composer require package/b:^2.0
composer remove package/c
...

When you’re done, make sure your composer.json and composer.lock files are valid using composer validate command.

Pros:

  • Reports conflicting dependencies as you add each of your packages
  • Quite fast

Cons:

  • You need to remember which packages you added or removed in your feature branch
  • Repeating these actions on each merge might be tedious and error-prone
  • Packages you added on your feature branch will be updated to most recent version matching the constraints (usually it should not be an issue)
  • Any package updates (via composer update) from your feature branch will be gone — you should re-run the update afterwards if needed

Method 3: Using external tool

Since above methods will not preserve the locked versions on packages that have been added on your feature branch, you may use an external tool such as Composer Git Merge Driver that automates this process, keeping your locked versions and minimizing the diff produced. Please refer to the official repository for installation and usage instructions.

Pros:

  • Works magically — in most cases you will not even notice the conflict

Cons:

  • Installation might be a bit too complicated to enforce it in the team

Additional notes

Please keep in mind that using Method 1 or 2 will discard the information about the exact package version added in your feature branch. This is because composer will lock the newest available version matching your constraints at the time of merge. If your package was constrained as ^1.0 and locked at 1.1.0 , it might be updated to 1.2.0 while performing these steps. Most of the time it should not produce any side effects, especially if your version constraints are valid.

--

--

No responses yet