Git Merging of Binary Files (in particular Xcode project files) - xcode

I've got an Xcode project under git and I've got a "experimental" branch off of my "master" branch. Both branches have diverged since the branch (so no fast-forwarding!), and I am merging "experimental" into my "master" branch.
I've setup a .gitattributes file to treat a particular Xcode file (project.pbxproj) as binary, as it should be treated as such. However, I'm not sure exactly how to merge it. I'm not sure what that particular file does, but if for example it handled which files were added to the project there's no way I can merge them, and therefore I'd have to manually add certain files back to the project (possibly not remembering them all). How do others handle this situation?
Also, I've read that you have to manually update binary files (obviously) or copy over different versions. However, once I'm into the merge process the file in my working copy is the "master" version. How can I access the "experimental" version? I can't check it out as it would disrupt the merging process.
Thanks for your time!

In general, for binary files, I recommend a copy-merge type of .gitattribute directive.
That is, I just copy the file coming from the remote end of a merge.
For .pbxproj files however, that may not be a good option, as described here.
Wrapping these files would only make the problem worse as far as I can tell - as you wold be completely unable to merge changes.
While you can merge project files in some cases, it's not something that you should
count on being able to do. The basic recommendation is to avoid editing the project files at the same time as someone else.
I like versionning projects files (like the ones for Eclipse), only if I know they are:
not modified that often (certainly not by each developers)
only using relative paths
no data specifics to one workstation
In short, if those files are not to be merged (or will only be trivial to merge), I want them in a VCS. In your case, that may not be so useful to have them in said VCS in the first place.
Note: as mentioned in the Git manual:
During the merge, the index holds three versions of each file. Each of these three "file stages" represents a different version of the file:
$ git show :1:file.txt # the file in a common ancestor of both branches
$ git show :2:file.txt # the version from HEAD.
$ git show :3:file.txt # the version from MERGE_HEAD

Related

Working in git with directories with the same name but different case in Windows

I want to pull from a git repo in Windows which has two directories, named Foo and foo. Both the folders have different contents.
As Windows is case insensitive and doesn't allow folders with same name but different case, how do I push to the git repo?
Short answer: You can’t do this easily.
By default, this is a restriction of the Windows subsystem. Unless you use lower level system calls, Windows cannot differ between different casing; so even if Git is able to keep track of the differences, it can’t communicate these difference to the file system.
As pointed out in the comments by phuclv, it is possible to reconfigure the Windows kernel to be case sensitive. In Windows 10, this even works for individual folders, so you could use this to add compatibility where you need it. However, the case sensitivity per folder is not inherited, so you will need to manually change this for the folders that Git creates which might be a bit bothersome and makes this mostly a workaround.
Instead, you could make the whole file system case-sensitive but that might have additional implications, so just be careful if you want to do that.
Also note that even if there is support for case-sensitive content on the lower level, most Windows applications, including built-in Windows tools, will probably not be able to work with this. So this will only allow you to work with these files from certain tools. My guess would for example that most GUI based Git tools simply won’t work here.
If you don’t want to make these modifications, then what you maybe also could do is create partial commits where you just add files to the correct folder (you need to rename it in-between to get the different casing). But that will be very impractical.
In my opinion, the best solution is to simply avoid using multiple files on folders with conflicting names. Even on case-sensitive systems, this will only make things more confusing. By avoiding this completely, you also make it easier for all other developers to interact with the project.
As a follow up to poke's answer, you need to split those directories apart into different names, or merge them correctly into one with the same name, depending on your needs of course. They can't have the same name other the case and have it work in Windows (in a clear and obvious manner anyway).
I accidentally ended up in this same boat. I'm not sure how, as I was using Windows the whole time, but at some point I changed the case on a directory in the repo and some files ended up remaining in a directory with the "old" name and some in one with the "new" name. On my Windows machine they were all under the new name, but I found this problem when I pulled the repo into Linux, and confirmed the split when I looked in my remote repo.
To fix this, I first cloned the to a separate location on my Windows machine. Doing so, all the files were there in one directory again, as apparently the two directories just get merged. I then renamed that problem directory to "temp", (using the TortoiseGit "rename" operation). Then, I cloned the repo to yet another location. At that point, the two directories were in fact split apart in Windows. I had a "temp", plus the directory with the "old" name.
As I really did want them in one directory (on all platforms!), I moved the files out of the old named directory into "temp", then deleted the "old" directory. Next, I renamed temp (using the TortoiseGit "rename" operation again) to the name I wanted everything within, committed and pushed again. Finally, I pulled the changes into my original repo (my Linux one) and checked what was on the remote. Everything was finally in agreement, so I deleted those temp clones and called it a day.

Xcode Project file git merge conflict

In Xcode, what's the best way to avoid Git conflict in the project file? (I am in manual git, not using Xcode interface for git)
I've cloned mapbox-ios-sdk from Github, hacked it, and now the remote master has changed. When I tried to pull the remote changes into my local, after merging there would be merge conflict in the project file. (Specifically, I mean the project.pbxproj in the .xcodeproj)
I do not really think project file should be put into the ignore, since if there are any new files the project file, the .pbxproj file seems to be changed. (Or am I just plain wrong and this file should be put to ignore? But obviously it wasn't on ignored in the mapbox-ios-sdk to begin with. People need the project file after all.) But I've also ran into this conflict before in my collaboration project with another collaborator and it's keeping me from using Git altogether.
Should I figure out how to manually resolve conflict or is there a better way to deal with this?
A lot of websites simply suggest to use a .gitattributes file with:
*.pbxproj merge=union
We're gonna try this approach before trying to use scripts. If we find any issues I'll be sure to update this post. Also if anyone else has comments to this approach please include.
.pbxproj will change when you add new files to the project. There will be conflicts if two or more collaborators add files at the same time (without getting one another's changes first). We are avoiding this in my project by following these steps before and after adding new files:
Before adding a file, commit your changes, then pull from the master so that you have the latest.If someone has added a file, you now have the latest .pbxproj
Add your file(s).
Immediately commit and push your changes up to the master (hopefully, before another collaborator has added another file).
It's wimpy, but we don't relish manually resolving .pbxproj conflicts.
Also, see this Stack Overflow question with excellent responses: How to use Git properly with XCode?
You should check my script xUnique, it is now the best solution to merge Xcode project file before Apple takes action on it.
What it does & How it works
convert project.pbxproj to JSON format
Iterate all objects in JSON and give every UUID an absolute path, and create a new UUID using MD5 hex digest of the path
All elements in this json object is actually connected as a tree
We give a path attribute to every node of the tree using its unique attribute; this path is the absolute path to the root node,
Apply MD5 hex digest to the path for the node
Replace all old UUIDs with the MD5 hex digest and also remove unused UUIDs that are not in the current node tree and UUIDs in wrong format
Sort the project file inlcuding children, files, PBXFileReference and PBXBuildFile list and remove all duplicated entries in these lists
see sort_pbxproj method in xUnique.py if you want to know the implementation;
It's ported from my modified sort-Xcode-project-file, with some differences in ordering PBXFileReference and PBXBuildFile
With different options, you can use xUnique with more flexibility
Check README file for more details.
In most case, you can fix the merge by following code, remove the lines which git adds:
#!/bin/bash
FILE={PRODUCT_NAME}.xcodeproj/project.pbxproj
sed '/======/d' $FILE | sed '/<<<<</d' | sed '/>>>>>/d' > temp
cat temp > $FILE
rm temp
but if you rename the group of your project and it leads to conflicts, you will manually delete the extra lines of your original group.
When this is caused by adding files from two or more collaborators/branches (which in my experience has always been the case to date and can be checked by looking at the diff) I do this:
Resolve the conflict by "use ours" or "use theirs" (whichever has added the most files).
Commit (this produces a repo that contains all the added files but some are not in the project).
Within Xcode use "Add Files To..." to add the missing files from the other collaborator/branch (they are easy to find as they are in the project directory and Xcode usefully highlights them for you).
Commit.
(I am not convinced step 2 is really necessary - but feels neater to me).
You can also use Mergepbx manually do it as follows
Install Mergepbx using brew.
brew install mergepbx
Run a command each time you have conflicts in your project file to automatically resolve them.
git mergetool --tool=mergepbx PROJECT.pbxproj
I need to get master code in my branch So, I was taking pull from master and I have a change in my .pbxproj file and master has different configuration. So, It was showing conflict in .pbxproj file. Follow these steps to resolve it
Open .pbxproj in Finder->
Right click on file Choose Show Package Contents->
Choose .pbxproj file and open it with TextEdit->
It will show conflict lines as below.
<<<<<<< HEAD"
// head changes
===========
// Your changes
>>>>>>>>
You have choose the right one from head Changes and your changes and Delete the rest.
Now commit you code
git add .
git commit -m"conflict resolved"
git status // Check you files status
If everything is fine the push you code.
git push
I made an app for fixing things in Xcode project files when these sort of things go wrong. You'd still have to do some manually merging first though, unfortunately.
https://itunes.apple.com/us/app/xsaviour/id1449886415?mt=12
If all else fails, while tedious. you can open your pbxproj in your git client, then manually, go file by file and ensure each one is either removed or has 4 lines (6 occurrences).
I had the same problem, so I wrote a tool in Swift that will resolve conflicts due to adding, deleting, moving, and renaming files and groups. You can set it up as a git merge driver so the script runs automatically when you merge a branch.
You can check it out here: XcodeMergeDriver

Relative file referencing with SVN on Windows

I have a project with a sizable codebase. Associated with that codebase is a large amount of documenation that needs to maintained at the same version as the source code and which also needs to be easily accessible from within the codebase. However when our build machine builds the codebase I do not want the length of our build process extended by having the build machine checking out hundreds of megabytes of development documentation which is not needed for the build.
If this was on Unix I could simply have a 'docs' directory at the peer level of the codebase's 'source' directory. Then individual projects in the source tree could reference documentation in the docs tree using symlinks, and when the build machine does a build it would just check out the source directory and so not waste time checking out the unneeded docs directory.
However using SVN on Windows I don't see any way to set this up in a sensible way at all since SVN doesn't support symbolic links on Windows, even though Windows has them.
The only workaround I've come up with so far is to create batch files in the source tree which use cmd.exe and a relative file reference to open the documentation files in the docs tree. It works, but for some reason I can't quite put my finger on it leaves a nasty taste in the mouth.
Can anyone think of a better way of achieving this?
After some research I think I have a solution using the externals property.
Firstly using the svn:external property to reference a directory in the same repository. Set this on trunk/Proj1 to create Proj1/Docs referencing the contents of DocsDir/Proj1Docs
../DocsDir/Proj1Docs Docs
This creates a disconnected child working copy inside Proj1/Docs which references /DocsDir/Proj1Docs. Proj1/docs must not previously exist as part of the outer working copy (which makes sense since that would make it part of two working copies at once). If you edit the contents of Proj1/Docs then executing svn status inside the 'parent' working copy will list the changes to the child working copy, but you have to commit the changes to the child copy separately. Which is not a big deal.
Secondly using the svn:external property to reference to a file in the same repository. Set this on trunk/Proj1 to create Proj1/Readme.txt which references DocsDir/Readme.txt.
../DocsDir/Readme.txt Readme.txt
In the case of a file reference the directory in which the referenced file is imported must already be part of the owning working copy. In this case no child working copy is created and if you edit the file it is commited seamlessly as part of the owning working copy.
In both cases the build machine can execute
svn checkout --ignore-externals <path>
to checkout our codebase without all the bulky documentation.
Can anyone see a problem with this strategy?

Git and pbxproj

I was looking at an open source Mac application, and they gave some suggested values for .gitignore. They were what I would expect...
However, they also suggested an entry into a .gitattributes file:
*.pbxproj -crlf -diff -merge
I'm not the most knowledgable in terms of git, so I was wondering - what exactly are the benefits of adding this line? What does do in particular? I've only seen this suggested in this one project, and if it was normal practice I would have expected to see it elsewhere right now. So I was curious about how it applies to the pbxproj file specifically.
The pbxproj file isn't really human mergable. While it is plain ASCII text, it's a form of JSON. Essentially you want to treat it as a binary file.
Here's what the individual flags do:
-crlf: don't use crlf <=> cr conversion
-diff: do not diff the file
-merge: do not attempt to merge the file
From the Pro Git book by Scott Chacon
Some files look like text files but
for all intents and purposes are to be
treated as binary data. For instance,
Xcode projects on the Mac contain a
file that ends in .pbxproj, which is
basically a JSON (plain text
javascript data format) dataset
written out to disk by the IDE that
records your build settings and so on.
Although it’s technically a text file,
because it’s all ASCII, you don’t want
to treat it as such because it’s
really a lightweight database — you
can’t merge the contents if two people
changed it, and diffs generally aren’t
helpful. The file is meant to be
consumed by a machine. In essence, you
want to treat it like a binary file.
A diff is oftentimes useful at commit time to check what has been changed. So I find it useful to keep the diffing ability but just prevent merging. So I use this in my .gitattributes file:
*.pbxproj -crlf -merge
On another note, has anybody tried using merge=union for pbxproj files? See: Should I merge .pbxproj files with git using merge=union?
I faced the problem of corruption *.pbxproj file after resolving merge conflicts manually. Or, more often, my files just 'disappeared' from the working tree after the merge. It drove me mad because we work in a team, so you can imagine how messy it can become very fast.
So, I have tested merge=union and it works well so far. I know that it can't help if files were deleted or renamed at the same time, but for adding new files it works as expected: there is no conflicts and files don't disappear after the merge. And it also saves quite a bit of time.
If you want to try it out, here is what I did.
1) Create a global .gitattributes file. Run in terminal:
touch ~/.gitattributes
git config --global core.attributesfile ~/.gitattributes
2) This command should open it in a text editor:
open ~/.gitattributes
3) When the file opens, add this line and save the file:
*.pbxproj binary merge=union
Done. Hope this will help new readers like it helped me.
I wrote a python script named xUnique to solve this merge conflicts problem.
This script do following things:
replace all 24 chars UUID to project-wide unique 32 chars MD5 digests, and remove any unused UUIDs(usually caused by careless merge before). This would prevent duplicate UUIDs because different machines/Xcode generate different UUIDs in this file. Xcode does recognize it and the project could be opened. During this process, remove all invalid lines in project file
sort the project file. I wrote a python version of sort-Xcode-project-file from Webkit team with more new features:
support to sort PBXFileReference and PBXBuildFile sections
remove duplicated files/refs
avoid creating new file even if no changes made, this makes less commits after using this script
More details and updates of xUnique, please refer to README

Xcode, git, and intermediate build folders

I would like two things:
to be able to change branches in git, and then Run or Build in Xcode without recompiling the entire project.
have git ignore intermediate build files during merge, so it won't ask me to resolve any conflicts.
Putting the intermediate builds folder outside the project, or using .gitignore to ignore that folder, accomplishes #2 but not #1; I have to rebuild the entire project when I change branches, even if I did not modify any files.
Well, you've answered #2 correctly yourself, so really your question only related to #1. I don't really see why Xcode would need to recompile things either - git won't change timestamp on unchanged files when switching branches.
Have you actually implemented the #2 solution, so that the entire problem isn't caused by git stomping on your build directory, which should be .gitignore'd?
You could define two variables:
MY_BRANCH_NAME = branch_foo (adapted in each branch)
TARGET_TEMP_DIR = $(CONFIGURATION_TEMP_DIR)/$(TARGET_NAME)$(MY_BRANCH_NAME).build (the same for all branches)
This way, the builds for your different branches will be made and keeped in separate folders, not needing to recompile everything because of a branch-to-branch config change.
You can do it in xcconfig files, or automatically define MY_BRANCH_NAME as an xcodebuild argument in a build script, among other means.
Xcode is going to do all of its data based on the timestamps of the files in question. If you replace the file with a newer file, then Xcode should notice that the timestamp of the file is newer than the timestamp of the build product and recompile it.
However, if you change it with an even older version of the source file, then it can't know that the build file isn't correct. It will just see that the build output is still newer than the source file, and so not recompile it.
In short, you can't know which files have definitely changed, and which have definitely not. You're better off doing a full clean+rebuild to make sure; otherwise you're going to lose time debugging when it doesn't work.

Resources