如何在 Xcode 中正确使用 Git?

我做 iphone 开发者已经有一段时间了,最近我把 git 加入到了我的工作流程中。到目前为止,我已经使用了在 http://shanesbrain.net/2008/7/9/using-xcode-with-git上找到的 git 设置。

这些设置告诉 git 排除 * 。合并的 pbxproj?有什么真正的原因吗?例如,当我向项目中添加一个文件并推送到原始文件时,我的同事开发人员在拉取时不会将该文件添加到他们的 xcode 项目中。然后,如果其中一个构建了一个发布,这个文件可能不包括在内。我是不是应该让 Git 来处理项目文件的合并?为什么或为什么这个文件应该在合并,以及如何正确处理的情况下,文件添加到项目?

46010 次浏览

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.

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

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

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.

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.