Best practices for Xcode + Git for multi-developer projects - xcode

I can create a repo and use GitHub / BitBucket fine for my own projects. I have had problems when collaborating with other developers or trying to fork a project on GitHub.
I am aware of other answers like Best practices for git repositories on open source projects but there are OSX / Xcode specific problems I want to know how to solve.
.DS_Store files can be a pain. You can use .gitignore to prevent, but what happens if they have already been included, or another developer adds them back in through a clumsy git command?
The .xcodeproj will have changes to the directory names and developer profiles for the other person. What's the best way to do merges or to avoid conflicts?
If I have forked or pulled from a github project, how can I clean up these issues and also minimise merge conflicts for the maintainer?
If people have an example .gitignore created for Xcode, or scripts they use to initialise their repos then that would be great!

Put .DS_Store in .gitignore. Then, if you haven't already, add .gitignore to the repo. (You should not ignore .gitignore.) Now all developers will ignore .DS_Store files. If any were added to the repo erroneously before you put .DS_Store in .gitignore, you can now remove them (in a commit) and they should stay out.
The xcodeproj is a directory. The only file in this directory that must be in the repository is the project.pbxproj file. I generally ignore all of the others by putting these lines in my .gitignore:
*.xcuserstate
project.xcworkspace/
xcuserdata/
You should avoid putting absolute paths in your build settings. Use relative paths.
Your Debug and Release builds should use iPhone Developer as the code signing identity, so that Xcode will automatically select the local developer's profile. When you want to create an IPA (for distribution), Xcode will offer to re-sign it with a different identity, at which point you can choose your distribution profile if you need to.
If you're trying to use a project from github that has made these mistakes, you can try to get the maintainer to fix them, or you can make sure you don't touch the .DS_Store files and the code signing identities in the same commits that you want to send upstream.

For the 2nd issue regarding the .xcodeproj and merge conflicts.
Using a .gitattributes file to specify that merge conflicts for all .pbxproj files should be handled using the merge=union strategy, which should mean that Git knows to merge in the changes from both sides of the conflict, taking the upstream changes first.
This article explains it in a bit more depth

I'll try one by one:
I. You need to use git filter-branch only if you need to remove the files from your history completely. If those files do not contain any credit card information, then i think the following should be enough:
git rm --cached .DS_Store
git commit -m "{Your message}"
then add this file to .gitignore and commit it.
This will commit the removal of the file from the repository but will keep the file in working directory. If you push it though and then somebody else will pull this commit, they might have their file removed, so you MUST communicate this.
By committing .gitignore you will prevent other developers from adding this file again.
If you're not a maintainer, then i don't think you should do anything, but address this issue to the maintainer.
II. I'm a strong believer that hidden files of any nature are most of the time not supposed to be put into the repository exactly for that reason. Therefore i think that you should do the same thing with .xcodeproj as with .DS_Store and put it into .gitignore and commit it. .gitignore is the exception for the rule above.
III. If those files are properly ignored , then there will be no issues in future with them. If they are already in the repo and somebody wants do such cleanup it should be done by maintainer and communicated inside the team.
Hope that helps!

git filter-branch might help you to remove unwanted files (.DS_Store files) from your repository -- see e.g. https://help.github.com/articles/remove-sensitive-data
If a clumsy git commit has added files you should be able to replay the corrected changesets onto a clean repository.

You're right in the sense that if a .DS_Store is already added the .gitignore won't be of much help however I think this is still a good resource for you and others.
When I start a project, I normally look at this list to see if there is a good .gitignore already existing. More specifically for you, this one is the Objective-C .gitignore.
Hopefully those resources are of some use.

As a Mac user you should download a tool like SourceTree which supports Git Flow. Git Flow will help you establish some best practices around how your collaborators will commit code to the repo and at the very least make merge conflicts less frequent and more manageable. For a set of gitignore files for various project types you can go to GitHub and download one that is ready to go. For Xcode they have it listed as Objective-C.gitignore. That is a good starting place and it even covers Cocoapods. If you're using external libraries, your project should use CocoaPods so that you can isolate that code and keep it outside of your repo and avoid git submodules.
Now when you find a file has made it into your repo like .DS_Store just remove it, and move on. Make sure you add it to the .gitignore file that is checked into the project.
As for xcodeproj... there shouldn't be that much customization within the file that is user specific since the above mentioned gitignore filters that out. If a scheme is to be shared make sure you check shared under Manage Schemes and you will check in files in that subdirectory. You should be using automatic selection of certificates so the only real choice is Developer or Distribution. You should also take advantage of variables provided within Xcode that avoid hardcoding complete paths. When trying to think of an example Plists came to mind, in this case, you might have written /Users/me/MyProject/Resources/MyProject.plist, but instead should use $(SRCROOT)/resources/MyProject.plist.

Related

XCode not showing files pulled from Source Control

I am working on a Swift based XCode project that is synced with Git based master repository. One of my team member has added few files, to the master which I have pulled.
I find those files in my project directory, but not in my project. When I am manually trying to add these files to the project, in that case, I am getting Modify tag on my project, then I am unable to pull new changes from master.
This is very annoying. How do I take care of this.
Also, how do we make sure as a team that everyone of us is working separately on different module, and make surely everyone is able to commit/pull each other's changes.
The project folder with M tag is prohibiting us from doing so.
Edit 1
I have followed steps for this as well
git rm -r --cached ProjectName.xcodeproj
git commit -m "Removed file that shouldn't be tracked"
Even after doing this,
I had made changes in File1.swift, with my team member's changes in File2.swift
He had already committed it, I wanted to pull those changes, however due to changes in File1.swift I was unable to pull it.In order to make it work, I had to discard changes in File1.swift and then only I was able to pull those changes. If this is so, then it is defeating the purpose of using git
Xcode project files
The Xcode project file, or to be more precise, the pbxproj file inside the xcodeproj container keeps track of all files (among other things). Unlike Eclipse, Xcode does not monitor your source/project folder for changes thus does not update the list of files which can lead to inconsistencies after a merge.
Merge conflicts
If your team member adds or removes a file in his local copy of the repository and pushes his changes to the server, he basically overwrittes the pbxproj and therefore updates the list of files of the Xcode project. If you pull those changes there are two possible scenarios:
Git can merge the changes automatically which usually means it will keep your copy of the pbxproj file without the updated file references.
Git cannot merge the changes and therefore indicates a merge conflict that you need to resolve yourself. Depending on the differences and amount of changes these merge conflicts can be pretty annoying to resolve. Sometimes it might be easier to just delete your copy of the pbxproj file, use the one from the server and reapply your local changes, e.g., add file references.
In both cases you might need to compare your pbxproj file against the one from the server and merge things by hand. And yes, your project file will be marked with an M (for modified) which is perfectly fine. Just commit your changes and you are good to go again.
One more thing: Whenever you want to pull changes from the git server you can either stash your changes (git stash) or commit them.
Avoiding merge conflicts
In case you want to avoid merge conflicts in the future, I recommend having a look at the following tools.
Cocoapods
Cocoapods can be used to modularise your project into smaller pieces by creating private Pods. The benefit of this technique: You do not have to keep track of newly added or removed files. Just run pod update and you are up and running again. There are several blog posts that describe this technique in detail, e.g., here
phoenx
At my former company we had a pretty huge codebase and ran into some trouble when using Cocoapods. Therefore, we have developed our own meta-build system called phoenx. Phoenx can generate Xcode project and workspace setups of arbitrary complexity. It uses metadata files (something like Podfiles and Podspecs) to generate the projects etc. . Build settings will be stored in xcconfig files. At the moment it does not provide any setup tool so you have to invest a bit of time to write the xcconfig and metadata files by hand. We are working on a more convenient way to use it though. If you want to give it a try you can install it via sudo gem install phoenx. Documentation is available on GitHub.
Hope that helps!

Git + LaTeX + BitBucket: Sharing image files

If I am version controlling my LaTeX docs and have a repo on bitbucket which I share with other conotributors, how do I share the png/jpg etc. files without having git tracking them?
Because every contributor should be able to compile it without LaTeX's draft check and visualize the complete paper with images, but it makes no sense to track such images with git (my .gitignore has a img/ line in it)
Check out the "Downloads" section of your Bitbucket repo. It is made for "adding any file that you would like to make available to your users, such as app binaries", which sounds pretty much like what you need. But you collaborators still have to download / unpack them manually.
Also, you can actually store binaries in Git repos. The problem is that they cannot be "delted" effectively due to Git internals and each binary file modification just duplicates all the bytes, even if you changed only one. So, if you don't change them frequently it's pretty ok. Bitbucket has a limitations on max repo size, so you'll get a warning when it is fool.
Another approach is to use Git Large File Storage which is especially created to handle binaries in Git repos. Unfortunately, it is not available on Bitbucket yet. If you can move your repo to Github consider this possibility.

How to delete files or folders in a git repository using Xcode?

My project had several folders that accidentally got added to my GitHub repository. I could delete them (one file at a time) from the GitHub website but that is tedious for a large number of files. I tried using git on the command line but it was conflicting with the Xcode source control updates. I would just like to do it graphically in Xcode if possible.
The closest Xcode question that I could find was this one, but it was about removing source control altogether, which I don't want to do.
I struggled with this for quite some time but it turned out to be fairly easy. I am sharing my answer below.
As long as you have your project already connected to a remote GitHub repository, all you have to do is use Finder to delete the files or folders.
Then go to Source Control and choose Commit and after that Push.
This will delete the files from your GitHub repository without you having to use the command line.

Delete an unreferenced image from repository in Xcode

I deleted default.png from my resources folder because I wanted a different image for the loading screen, but I just deleted the reference which was apparently a dumb thing to do. I dragged the new image into resources and tried to change the name to Default.png, but it won't let me, which I think is because the first Default.png is still in the repository somewhere. Anyway, how do I delete that image(and others with which I have probably done the same thing) from the repository when it is no longer visible in xcode?
What kind of repository is it? Subversion? Git?
The SCM integration in Xcode is great for checking out files and committing changes without having to leave the IDE, but it's hardly a full-blown GUI front end to either svn or git. It may be possible to fix your mistake by adding the file back to the project and then deleting it in Xcode in such a way that Xcode will remove it from the repository for you, but the simple solution is to just delete the file from the repository yourself by using the appropriate version control command. For example, if you're using Subversion you could:
svn remove default.png
to remove the file from your version of the repository. When you commit your changes, the file will be deleted in that version. (It'll still exist in previous versions -- that's the whole point of SCM, after all.)
After that, you can create the new file and add it to both the project and the repository in the usual way.
You need to manually go into your app's file structure and delete the image files themselves. Also, it is usually a good idea to "clean" the app whenever you remove files or references to files from an XCode project, since XCode can be a bit temperamental about removing files; the key combination for this is
Hope this fixes your problem.
Clean all targets should work (at least it worked for me). You can try the following:
a) delete the reference from "Copy Bundle resources" of your target
b) delete the app from Simulator/Device
c) clean all targets
Caleb is absolutely right. That fixed the problem here as well for the most files.
An easier way to get an overview of the accidentally un-deleted files, is creating a bookmark of the working copy with Versions (SVN Software - in case you use SVN). There you can detect the problematic files grafically and delete them. I always have to do this after restructuring the project folder.

Best approaches to versioning Mac "bundle" files

So you know a lot of Mac apps use "bundles": It looks like a single file to your application, but it's actually a folder with many files inside.
For a version control system to handle this, it needs to:
check out all the files in a directory, so the app can modify them as necessary
at checkin,
commit files which have been modified
add new files which the application has created
mark as deleted files which are no longer there (since the app deleted them)
manage this as one atomic change
Any ideas on the best way to handle this with existing version control systems? Are any of the versioning systems more adept in this area?
Mercurial in particular versions based on file, not directory structure. Therefore, your working tree, which is a fully-fledged repository, doesn't spit out .svn folders at each level.
It also means that a directory that is replaced, like an Application or other Bundle, will still find it's contents with particular file names under revision control. File names are monitored, not inodes or anything fancy like that!
Obviously, if a new file is added to the Bundle, you'll need to explicitly add this to your repository. Similarly, removing a file from a Bundle should be done with an 'hg rm'.
There aren't any decent Mercurial GUIs for OS X yet, but if all you do is add/commit/merge, it isn't that hard to use a command line.
For distributed SCM systems like git and mercurial shouldn't be a problem as Matthew mentioned.
If you need to use a centralized SCM like Subversion or CVS, then you can zip up (archive) your bundles before checking them into source control. This can be painful and takes an extra step. There is a good blog post about this at Tapestry Central:
Mac OS X bundles vs. Subversion
This article demonstrates a ruby script that manages the archiving for you.
An update from the future:
If I recall, the problem with managing bundles in SVN was all the .svn folders getting cleared each time you made a bundle. This shouldn't be a problem any more, now that SVN stores everything in a single .svn folder at the root.
Bringing this thread back to daylight, since the October 2013 iWork (Pages 5.0 etc.) no longer allows storing in 'flat file' (zipped), but only as bundles.
The problem is not the creation of version control hidden folders inside such structures (well, for svn it is), but as Mark says in the question: getting automatic, atomic update of files added or removed (by the application, in this case iWork) so I wouldn't need to do that manually.
Clearly, iWork and Apple are only bothered by iCloud usability. Yet I have a genuine case for storing .pages, .numbers and .keynote in a Mercurial repo. After the update, it blows everything apart. What to do?
Addendum:
Found 'hg addremove' that does the trick for me.
$ hg help addremove
hg addremove [OPTION]... [FILE]...
add all new files, delete all missing files

Resources