Merging .pbxproj Easily with Vim

When using git for source control while working on a project in Xcode, as long as you track your .pbxproj in git, it's inevitable that at some point you'll get a merge conflict.

My general procedure used to be to resolve all merge conflicts in Xcode. With regular source files this is possible, even though it turns out to be way faster with vim, but when a conflict crops up in the .pbxproj, it's impossible.

NOTE: the .pbxproj is not really intended to be edited by humans. It's possible to mangle the file to a point where it won't work any more, and then it will be difficult to figure out how to fix it. However, in my experience I've always been able to resolve all conflicts and end up with a working project. But keep in mind, this is the reason Google's policy is to keep the .pbxproj out of version control. Anyway, proceed with caution! Make sure you can easily get back to a working state prior to the merge if things go awry.

I'm going to make a suggestion which was not obvious to me when I first started using git: Never resolve merge conflicts in Xcode.

This will make your merge process much more pleasant in general, and will save a good amount of headache.

If you come across a conflict while you're using git from the terminal, you'll see something like this:

user$ git pull origin develop
Auto-merging MyProject/MyProject.xcodeproj/project.pbxproj
CONFLICT (content): Merge conflict in MyProject/MyProject.xcodeproj/project.pbxproj
Automatic merge failed; fix conflicts and then commit the result.

At this point, you need to fix the conflicts.

Good news: you're already in terminal, so vim is super handy. Other good news: git just told you the file that conflicts.

Since you know where the file lives, hardly any thought goes into opening the file with vim:

vim MyProject/MyProject.xcodeproj/project.pbxproj -- and of course you have tab-completion, too.

So with the file open, your goal is to find & resolve the conflicts.

A conflict looks like this:

732AFB1B167ABC3F00D28EF7 /* Filename.jpg ...  (A bunch of stuff that doesn't conflict)
<<<<<<< HEAD:project.pbxproj
732AFB1B167ABC3F00D28EF7 /* NextFilename.jpg ...  (New stuff in YOUR branch)
732AFB1B167ABC3F00D28EF7 /* OtherFilename.jpg ...  (New stuff in THEIR branch)
>>>>>>> Message on merged commit:project.pbxproj
732AFB1B167ABC3F00D28EF7 /* MoreFilenames.jpg ...  (A bunch of stuff that doesn't conflict)

In project.pbxproj, you'll be looking at a slew of numbers that are pretty hard on the eye. It's how Xcode keeps track of all the files & references in the project, and a conflict will usually occur when two people have added resources to the project.

But at this point you need to apply a bit of thought to which of the changes you want to keep. Everything under the <<<< will be from your own branch, the one you were merging into. Everything under the ==== will be from the other branch, the one you were merging from. If the conflict is from multiple resource additions, you'll probably want to keep both changes.

In vim, search is performed with the / key. Here's the bread and butter of merging the conflics:

Type /<<< and press enter. You'll land on the first conflict.

Want to keep both changes? Then all you need to do is remove git's conflict markers, and leave all the text that's in between. Go into Visual Line edit mode (shift+v), and press x to delete the whole line.

Now delete the other markers:
/===, enter, shift+v, x.
/>>>, enter, shift+v, x.

Check that there are no other conflicts:
/<<<, enter. If you get another hit, resolve the conflict as before.

When there are no more conflicts, tap the colon and type wq (write, quit):
:wq, enter.

You'll land back in terminal, ready to commit the merge:
git add .
git commit -m "Resolved merge conflicts."

And that's it! Usually you'll have to close the project and re-open it for Xcode to recognize that the .pbxproj is now valid.



blog powered by Pelican