1

This question arises from reading and commenting on another question: Git ignore file for Xcode projects

While Adam's answer (and accompanying gist) appears definitive, I cannot actually get it to work. My understanding of the gitignore syntax seems incomplete.

My setup: OSX 10.8.2, git version 1.7.9.6 (Apple Git-31.1). There are no global gitignore files.

I am testing on a new project created with the OSX Application template in Xcode 4.5.

This is the test project file layout:

./.gitignore
./MyApp
./MyApp/AppDelegate.h
./MyApp/AppDelegate.m
./MyApp/en.lproj
./MyApp/en.lproj/Credits.rtf
./MyApp/en.lproj/InfoPlist.strings
./MyApp/en.lproj/MainMenu.xib
./MyApp/main.m
./MyApp/MyApp-Info.plist
./MyApp/MyApp-Prefix.pch
./MyApp.xcodeproj
./MyApp.xcodeproj/project.pbxproj
./MyApp.xcodeproj/project.xcworkspace
./MyApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
./MyApp.xcodeproj/project.xcworkspace/xcuserdata
./MyApp.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad
./MyApp.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
./MyApp.xcodeproj/xcuserdata
./MyApp.xcodeproj/xcuserdata/admin.xcuserdatad
./MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes
./MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/MyApp.xcscheme
./MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist

I set up the test project with source control disabled, then initialise the git repo manually in Terminal. The working directory is the project directory:

git init
git add .
git commit -m "first commit"

which returns a list of committed files:

13 files changed, 5204 insertions(+)
create mode 100644 MyApp.xcodeproj/project.pbxproj
create mode 100644 MyApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
create mode 100644 MyApp.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
create mode 100644 MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/MyApp.xcscheme
create mode 100644 MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist
create mode 100644 MyApp/AppDelegate.h
create mode 100644 MyApp/AppDelegate.m
create mode 100644 MyApp/MyApp-Info.plist
create mode 100644 MyApp/MyApp-Prefix.pch
create mode 100644 MyApp/en.lproj/Credits.rtf
create mode 100644 MyApp/en.lproj/InfoPlist.strings
create mode 100644 MyApp/en.lproj/MainMenu.xib
create mode 100644 MyApp/main.m

After each .gitignore test I reset by deleting the .git folder and reinitialising.

If .gitignore is this

AppDelegate.h

then
./MyApp/AppDelegate.h
is excluded

If I change .gitignore to

AppDelegate*

Both of these are excluded
./MyApp/AppDelegate.h
./MyApp/AppDelegate.m
as expected

If my .gitignore is this:

AppDelegate*  
!AppDelegate.m  

Again, only AppDelegate.h is excluded

This is all as expected

However I can't get reincludes to work when the exclusion rule is a containing folder

For example if my .gitignore is this

xcuserdata  

then these files are excluded

./MyApp.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate

./MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/MyApp.xcscheme
./MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist

Now if I want to re-include the contents of xcschemes

xcuserdata  
!xcschemes

the result is the same - !xcschemes appears to be ignored.

similarly if .gitignore is

en.lproj

these files are excluded

./MyApp/en.lproj/Credits.rtf
./MyApp/en.lproj/InfoPlist.strings
./MyApp/en.lproj/MainMenu.xib

If I wanted to reinclude one of them

en.lproj  
!Credits.rtf  

the same files are excluded: !Credits.rtf is ignored.

the manpage for gitignore just says this:

An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again. If a negated pattern matches, this will override lower precedence patterns sources.

but I am not sure what "lower precedence patterns sources" means, or whether it is relevant here.

What am I missing?

update
Adam has updated his gitignore gist to take account of these issues. I have wrapped his changes into my fork, and also added some notes on how to add or change such a .gitignore when an XCode project is already under git control.

Community
  • 1
  • 1
foundry
  • 31,615
  • 9
  • 90
  • 125
  • Non-path-exclusions (like just `xcuserdata` instead of `xcuserdata/*`) will not be overridden by negative patterns. More about this on the [gitignore man page](http://git-scm.com/docs/gitignore) – Nevik Rehnel Dec 05 '12 at 20:52
  • 1
    The precedence of pattern sources is not relevant if you only use one source, namely .gitignore. I guess the issue is that Git does not descend into ignored directories, hence it will never find xcschemes to include it – Lars Noschinski Dec 05 '12 at 21:11
  • Thanks @cebewee and Nevik, that all makes sense, improves my reading of the man page and agrees with my tests. See also my comments to Michas' answer for my current solution. – foundry Dec 06 '12 at 00:37
  • @HeWas good work tracking this down - apologies that my gitignore didn't quite work. I tested it very carefully to start with, but I guess one of the later modifications I didn't test it well enough :( – Adam Feb 08 '13 at 12:51
  • @Adam, i finally got around to updating [my fork of your .gitignore](https://gist.github.com/foundry/4383910) - with your (untested) changes, thanks. I also added some notes on how to get this to work with an existing XCode project. – foundry Jun 02 '13 at 10:58

1 Answers1

2

Git traverses your file system tree and tries to match your ignore rules. If the rules match a directory that directory is, well, ignored. That means that git will not even look at any contents of it, that's why any exception inside of it will be ignored.

Getting around this is a bit tricky and not always possible.

/MyApp.xcodeproj/xcuserdata/*
!/MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/
/MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/*
!/MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/
/MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/*
!/MyApp.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/MyApp.xcscheme

The above .gitignore will ignore all files in /MyApp.xcodeproj/xcuserdata/ except for MyApp.xcscheme.

Often it is also possible to pretty much ignore those exceptions and only specify what you actually want to ignore.

michas
  • 25,361
  • 15
  • 76
  • 121
  • Thanks, very informative. I have decided not to negate. Only now I have to be precise what I am excluding, to the file level. And I find this to be quite a dark art. For example, do we want to track xcschememanagement.plist or not? The only files I can be certain we don't want from xcuserdata/ are UserInterfaceState.xcuserstate and (i think) xcdebugger. I would have thought we want to keep everything to do with xcschemes/, but I couldn't be certain what every one of those files will be. [This gist](https://gist.github.com/4220444) is my current solution for xcode .gitignore – foundry Dec 05 '12 at 23:48
  • @HeWas yeah, this is the main problem: most of the files are undocumented (thanks, Apple) - and a lot of them APPEAR unwanted, but later turn out to be "required" because of data that Apple only "occasionally" inserts in there (i.e. it's only when you have a very complex project that you discover what's wrong) – Adam Feb 08 '13 at 12:52
  • @michas - but this behaviour only applies to DIRETORIES, right? (if so: that's annoyingly illogical). i.e. where we ignore "*.something", we can then on a LATER line whitelist "a.something" -- and this works? – Adam Feb 08 '13 at 12:54