99

I have been an iphone developer for a while, and I have recently been including git in my workflow. I have used git settings found on http://shanesbrain.net/2008/7/9/using-xcode-with-git for my workflow so far.

Those settings tell git to exclude *.pbxproj from merges? Is there a real reason for doing this? For example, when I add a file to the project and push to origin, my fellow developers will not have that file added to their xcode project when they pull. Then if one of them builds a release this file may not be included. Shouldn't I just let git handle the merges for the project file? Why or why not this file should be in merges and how to properly handle the situation when files are added to the project?

Cœur
  • 37,241
  • 25
  • 195
  • 267
rickharrison
  • 4,867
  • 4
  • 35
  • 40
  • 10
    I don't work with XCode, but if *.pbxproj files are anything like Visual Studio's *.csproj files (somewhat of a list of files) this setting does seems rather idiotic to me. It sounds like someone was tired of merge conflicts when two people added files to the project and thought the best solution was to screw everything up... – R. Martinho Fernandes Apr 10 '10 at 23:15
  • The issue with XCode (not sure about Visual Studio) is that .pbxproj files are barely human-readable, so it doesn't make sense to resolve conflicts by hand. – Tom Apr 10 '10 at 23:34
  • 7
    *.pbxproj files are actually pretty well structured, you just have long stretches between the block end and start segments. The saving grace is that the file has very well placed line breaks so it's hard to mess it up just modifying lines and automerge generally works very well. It also means the merge blocks are generally easy to understand - you can see one side with a few sets of files added, the other with different sets of files added. – Kendall Helmstetter Gelner Apr 11 '10 at 02:03

5 Answers5

139

I have worked on iPhone applications full time since the SDK launch, most of that time spent working on teams with multiple developers.

The truth is that it's way more harmful to disallow merging of that .pbxproj file than it is helpful. As you say, when you add a file unless other people get that file, they have to also add it to their project - in an application of any size, that sucks and it also takes away a huge benefit of source code control in that you cannot really revert to a complete earlier project state just through git.

The .pbxproj file is simply a property list (similar to XML). From experience, just about the ONLY merge conflict you were ever get is if two people have added files at the same time. The solution in 99% of the merge conflict cases is to keep both sides of the merge, which for git at least simply involves removing any >>>>, <<<<, and ==== lines. In fact this is so common that I have created a simple shell script to fix a .pbxproj file in a merge state from git, I run this from within the project directory (at the Classes level):

#!/bin/sh

    projectfile=`find -d . -name 'project.pbxproj'`
    projectdir=`echo *.xcodeproj`
    projectfile="${projectdir}/project.pbxproj"
    tempfile="${projectdir}/project.pbxproj.out"
    savefile="${projectdir}/project.pbxproj.mergesave"

    cat $projectfile | grep -v "<<<<<<< HEAD" | grep -v "=======" | grep -v "^>>>>>>> " > $tempfile
    cp $projectfile $savefile
    mv $tempfile $projectfile

Worst case if it fails (you ask XCode to load the project and it fails to load), you simply delete the .pbxproj file, check out the master from git, and re-add your files. But I've never had that happen in many months of use with this script, again working full time on iPhone applications with several other developers.

Another option (pointed out in comments below) that you can try using in place of the script, is to add this line to a .gitattributes file:

*.pbxproj text -crlf -diff -merge=union

Then git will always take both sides of a merge for the .pbxproject files, having the same effect as the script I provided only without any extra work.

Lastly, here is my complete .gitignore file, showing what I do have it set to ignore as there are a few things you don't want - in my case really just emacs remnants and the whole build directory:

# xcode noise
build/*
*.pbxuser
*.mode1v3
*~

# old skool
.svn

# osx noise
.DS_Store
profile
Adam S
  • 16,144
  • 6
  • 54
  • 81
Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • 3
    Do you use a .gitattributes file at all for your xcode project? And thank you for your insight. I think it will be much easier to try merging the pbxproj files in the future. – rickharrison Apr 11 '10 at 15:52
  • 1
    To date we have not been, though some aspects of that look interesting - but the people I have worked with have not been advanced git users, and so advocation of advanced features is not strong. – Kendall Helmstetter Gelner Apr 12 '10 at 07:08
  • 1
    “The .pbxproj file is simply JSON (similar to XML).” Actually, it's an OpenStep-formatted property list. Same basic ideas as JSON, but the syntax differs in a few places. – Peter Hosey Apr 28 '10 at 20:22
  • Thanks Kendall - this issue has been a thorn in my side for a long time, came across this answer and it clarifies things. – ambertch Jan 29 '11 at 00:13
  • Doing that is good, but it can duplicate files. Just pay attention to warnings. – Matías Marquez Feb 01 '12 at 14:16
  • I've never had it duplicate files, now in years of using this technique... duplication would only occur if two people added files with the same name. So in that case it would not be the technique duplicating files, but the developers... – Kendall Helmstetter Gelner Feb 01 '12 at 17:54
  • This looks so promising! Try as I might, I can't get Xcode to play nice after manually resolving this. "Project Foo.xcodeproj cannot be opened because the project file cannot be parsed." (I am using a scratch branch to try this out, so no harm no foul.) The conflicts in this file weren't terribly drastic (to me, at least) so I'm not sure why this isn't working out. – Joe D'Andrea May 31 '12 at 20:24
  • Can you post a diff somewhere between your hand-revision and the local version? The "cannot open file" is usually a sign that one of the merge lines was not removed property. What are you using to accept all of the changes to both sides of the merge? If you are using my script it may have to be slightly altered depending on how your pre-merge files look. – Kendall Helmstetter Gelner May 31 '12 at 22:37
  • 1
    Another thing to try - set merge=union: http://stackoverflow.com/questions/2729109/should-i-merge-pbxproj-files-with-git-using-merge-union – Kendall Helmstetter Gelner May 31 '12 at 22:44
  • 1
    I agree with @KendallHelmstetterGelner instead of running your script, which removes those special lines, you could update your .gitattribute with the `union` switch: `*.pbxproj text/plain -crlf -diff -merge union`. – Besi Jul 16 '12 at 12:09
  • Thanks, but you should really try out using the -merge union .gitattribute setting instead, then you need do nothing... – Kendall Helmstetter Gelner Aug 16 '12 at 17:37
  • I tried using the .gitattributes way, but it gave me an error saying "text/plain is not a valid attribute name". Any suggestions? – Tony Aug 24 '12 at 20:49
  • what version of git are you using? I have 1.7.6, that line works OK in a .gitattribute file I am using. Are you sure you have it in there and not .gitignore? What causes the error to appear? You may well be better of putting your problem in a question here on StackOverflow, so a thousand git experts can descend upon it with good advice. – Kendall Helmstetter Gelner Aug 24 '12 at 22:03
  • Hi Kendall, I'm using git version 1.7.9.6 (Apple Git-31.1). Is it .gitattribute or .gitattributes? Everything I searched for turned up with the s as part of the file name. The error appears after I've saved the .gitattributes file and then run a "git add .gitattributes". – Tony Aug 24 '12 at 22:51
  • 1
    I think you are right about the name, I had my file wrong. I'll do further research on why that is not working. – Kendall Helmstetter Gelner Aug 25 '12 at 04:44
  • 1
    Corrected the example above. text/plain becomes just "text", that's what I get from copying code from elsewhere without a real check. Thanks @tony for asking about the problem. – Kendall Helmstetter Gelner Aug 25 '12 at 04:49
  • Thanks again Kendall ;) I checked the man on gitattributes and there wasn't anything regarding plain in there, so just "text" seems right to me as well! – Tony Aug 25 '12 at 05:46
  • It appears that when adding -crlf -diff git treat it as binary? – huggie Oct 16 '12 at 08:00
  • @huggie -crlf -diff makes git treat a file as binary. You can even replace it with binary which makes me wonder why the answer has "text" in it? Also as I understand it "-merge" does unset merge, so if you want union merge you need "merge=union". So my .gitattributes looks like this: ".pbxproj -crlf -diff merge=union" – Yanik Nov 08 '12 at 10:56
  • crlf just tells it how to treat the end of line markers. I fixed the example to add the "=" which was missing. – Kendall Helmstetter Gelner Nov 08 '12 at 14:55
10

This works for me in Xcode 4.6 and Git 1.7.5.

Add and commit .gitattributes file with this:

*.pbxproj binary merge=union

I've tested this with another team member and works great.

Taken from: http://robots.thoughtbot.com/post/33796217972/xcode-and-git-bridging-the-gap

oneyenjug
  • 109
  • 2
  • 5
10

Frankly, the existing answers are misleading.

If you never delete or rename files, then using the merge=union strategy, which just combines the differences in different commits directly, is a good idea.

However, in the real world, we do need to delete or rename files sometimes. Merging the differences without any modification would make a lot of problems under these situations, and these problems usually lead to the "Workspace Integrity - Couldn't load project" issue, which makes you even not able to run the project.

The best solution I got so far:

1) Design the project well and add all the needed files at the beginning, so you would seldom need to change the project.pbxproj.

2) Make your features tiny. Don't do too many things in a branch.

3) For any reason, if you need to modify the file structure and get conflicts in project.pbxproj, use your favorite text editor to solve them manually. As you make your tasks tiny, the conflicts might be easy to solve.

Brian
  • 30,156
  • 15
  • 86
  • 87
4

The short answer is that even if you don't include that line in .gitattributes, you may not be able to easily merge two modified versions of a .pbxproj. It's better for git to treat it as a binary.

See here for details: Git and pbxproj

Update: Even though the git book still agrees with this answer, I no longer do. I version control my .pbxproj just like any other non-binary source file.

Community
  • 1
  • 1
Tom
  • 3,831
  • 1
  • 22
  • 24
  • sounds like you could set up a commit filter to send the file through `simplejson` or some such tidier on its way to the index. It would still not be guaranteed to work though. – intuited Apr 10 '10 at 23:28
  • 1
    It's not a JSON formatted file. Looks similar but have many difference in detail. – eonil Jul 21 '12 at 13:42
  • It says its JSON in the git book, but it looks like it's wrong. http://git-scm.com/book/ch7-2.html – huggie Oct 16 '12 at 06:51
  • 1
    OK. `.pbxproj` file is actually an old style NeXT/Cocoa PList file, which is older, and defined a lot earlier than JSON, and now deprecated by Apple. (but they are still using it in some places) The mention about the file in the book is completely wrong. I removed down vote because you mentioned it explicitly. – eonil Jan 26 '13 at 08:56
2

I did create a Python script that can handle merge conflicts in XCode Project files.

If you want to try it, you can check it out here: https://github.com/simonwagner/mergepbx

You will have to install it as a merge driver, so it gets called automatically when you have a merge conflict in your project file (the README.md will tell you how to do that).

It should work much better than using merge=union as mergepbx understands the semantics of your project file and therefore will resolve the conflict correctly.

However the project is still alpha, don't expect it to understand every project file that is out there.

Simon
  • 1,521
  • 2
  • 13
  • 20
  • downvoted because the linked resource is not maintained (newest file last updated in 2016) and furthermore doesn't work on the current version of python provided by macOS – lewis Oct 17 '22 at 16:28