├── Resource ├── English.lproj │ └── InfoPlist.strings ├── DocSet-Info.plist ├── version.plist └── Info.plist ├── Documentation └── Traversal Sequences.png ├── Test ├── Support │ └── Ext.rb ├── Unit │ ├── GITPackObject.rb │ ├── GITBranch.rb │ ├── GITPackCollection.rb │ ├── GITPackedRefsEnumerator.rb │ ├── NSData+DeltaPatching.rb │ ├── GITLooseObject.rb │ ├── GITTreeItem.rb │ ├── GITActor.rb │ ├── GITTag.rb │ ├── GITBlob.rb │ ├── GITTree.rb │ ├── GITPackIndex.rb │ ├── GITPackFile.rb │ ├── GITRef.rb │ ├── GITDateTime.rb │ ├── GITCommit.rb │ ├── GITPackIndexWriter.rb │ ├── GITPackFileWriter.rb │ └── GITRefResolver.rb └── TestHelper.rb ├── Source ├── Git_Prefix.pch ├── GITError.m ├── Category │ ├── NSRange │ │ ├── NSRangeEnd.h │ │ └── NSRangeEnd.m │ ├── NSData │ │ ├── NSData+CRC32.h │ │ ├── NSData+DeltaPatching.h │ │ ├── NSData+Searching.m │ │ ├── NSData+Searching.h │ │ ├── NSData+Compression.h │ │ ├── NSData+SHA1.h │ │ ├── NSData+DeltaPatching.m │ │ ├── NSData+SHA1.m │ │ └── NSData+CRC32.m │ └── NSTimeZone │ │ ├── NSTimeZone+Offset.h │ │ └── NSTimeZone+Offset.m ├── GITPackFilePlaceholder.h ├── GITPackIndexPlaceholder.h ├── GITPackFileWriterPlaceholder.h ├── GITPackIndexWriterPlaceholder.h ├── GITPackIndexWriter+Shared.h ├── GITActor+Parsing.m ├── Git.h ├── GITPackFileWriterPlaceholder.m ├── GITPackFileVersionTwo.h ├── GITBlob.m ├── GITPackIndexWriterPlaceholder.m ├── GITPackIndexWriter+Shared.m ├── GITPackObject.m ├── GITBlob.h ├── GITBranch.m ├── GITActor.m ├── GITRef.m ├── GITActor+Parsing.h ├── GITPackFile.m ├── GITPackedRefsEnumerator.h ├── GITPackIndexWriterObject.m ├── GITPackIndexVersionTwo.h ├── GITDateTime.m ├── GITPackIndexVersionOne.h ├── GITBranch.h ├── GITTree.h ├── GITActor.h ├── GITPackIndexWriterVersionOne.h ├── GITPackIndexPlaceholder.m ├── GITError.h ├── GITRef.h ├── GITPackFilePlaceholder.m ├── GITPackFile.h ├── GITPackObject.h ├── GITPackIndex.m ├── GITTreeItem.m ├── GITLooseObject.m ├── GITTag.h ├── GITLooseObject.h ├── GITPackedRefsEnumerator.m ├── GITPackCollection.h ├── GITPackIndexWriter.m ├── GITPackIndexWriter.h ├── GITPackCollection.m ├── GITObject.m ├── GITPackIndexWriterObject.h ├── GITPackFileWriterVersionTwo.h ├── GITPackFileWriter.m ├── GITRefResolver.h ├── GITCommitEnumerator.h ├── GITRevList.h ├── GITObject+Parsing.m ├── GITPackIndexWriterVersionTwo.h ├── GITCommit.h ├── GITPackFileWriter.h ├── GITDateTime.h └── GITPackIndexWriterVersionOne.m ├── .gitmodules ├── .gitignore └── Vendor └── OmniGroup └── OmniBase ├── objc.h ├── NSError-OBExtensions.h ├── OBBacktraceBuffer.h ├── OBBacktraceBuffer.m ├── OBError.h └── assertions.m /Resource/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Documentation/Traversal Sequences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffgarside/Git.framework/HEAD/Documentation/Traversal Sequences.png -------------------------------------------------------------------------------- /Test/Support/Ext.rb: -------------------------------------------------------------------------------- 1 | class File 2 | def self.write(file, content) 3 | File.open(file, 'w') { |f| f.write content } 4 | end 5 | end -------------------------------------------------------------------------------- /Source/Git_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Git' target in the 'Git' project. 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Website"] 2 | path = Website 3 | url = git@github.com:geoffgarside/Git.framework.git 4 | [submodule "Documentation/doxyclean"] 5 | path = Documentation/doxyclean 6 | url = git://github.com/geoffgarside/doxyclean.git 7 | -------------------------------------------------------------------------------- /Source/GITError.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITError.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 15/09/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITError.h" 10 | 11 | NSString *GITErrorDomain = @"uk.co.geoffgarside.framework.Git"; 12 | -------------------------------------------------------------------------------- /Source/Category/NSRange/NSRangeEnd.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSRangeEnd.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 06/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | NSUInteger NSRangeEnd(NSRange range); 13 | -------------------------------------------------------------------------------- /Source/GITPackFilePlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFilePlaceholder.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackFile.h" 10 | 11 | 12 | @interface GITPackFilePlaceholder : GITPackFile { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Source/GITPackIndexPlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexPlaceholder.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndex.h" 10 | 11 | 12 | @interface GITPackIndexPlaceholder : GITPackIndex { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Source/Category/NSRange/NSRangeEnd.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSRangeEnd.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 06/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "NSRangeEnd.h" 10 | 11 | 12 | NSUInteger NSRangeEnd(NSRange range) { 13 | return (range.location + range.length); 14 | } 15 | -------------------------------------------------------------------------------- /Source/GITPackFileWriterPlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFileWriterPlaceholder.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackFileWriter.h" 10 | 11 | 12 | @interface GITPackFileWriterPlaceholder : GITPackFileWriter { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriterPlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriterPlaceholder.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexWriter.h" 10 | 11 | 12 | @interface GITPackIndexWriterPlaceholder : GITPackIndexWriter { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.xcodeproj/*.pbxuser 3 | *.xcodeproj/*.mode1v3 4 | *.xcodeproj/*.perspectivev3 5 | *.xcodeproj/*.tm_build_errors 6 | *.xcodeproj/*.xcworkspace 7 | *.xcodeproj/xcuserdata 8 | *.TM_Completions.txt.gz 9 | build/ 10 | Test/Tmp 11 | Documentation/ 12 | !Documentation/doxyclean 13 | !Documentation/*.graffle 14 | !Documentation/*.png 15 | !Documentation/*.txt 16 | -------------------------------------------------------------------------------- /Resource/DocSet-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | uk.co.geoffgarside.framework.Git.docset 7 | CFBundleName 8 | Git Framework 9 | DocSetFeedName 10 | Git Framework Documentation 11 | 12 | 13 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriter+Shared.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriter+Shared.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 06/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexWriter.h" 10 | 11 | 12 | @class GITObjectHash; 13 | @interface GITPackIndexWriter (Shared) 14 | 15 | - (void)addObjectHashToFanoutTable: (GITObjectHash *)sha1; 16 | - (NSInteger)writeFanoutTableToStream: (NSOutputStream *)stream; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Resource/version.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildVersion 6 | 2 7 | CFBundleShortVersionString 8 | 1.0 9 | CFBundleVersion 10 | 1 11 | ProjectName 12 | DevToolsWizardTemplates 13 | SourceVersion 14 | 15920000 15 | 16 | 17 | -------------------------------------------------------------------------------- /Vendor/OmniGroup/OmniBase/objc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2007-2008 Omni Development, Inc. All rights reserved. 2 | // 3 | // This software may only be used and reproduced according to the 4 | // terms in the file OmniSourceLicense.html, which should be 5 | // distributed with this project and can also be found at 6 | // . 7 | // 8 | // $Id$ 9 | 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+CRC32.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+CRC32.h 3 | // 4 | // Created by Geoffrey Garside on 08/11/2010. 5 | // Believed to be Public Domain. 6 | // 7 | // Methods extracted from source given at 8 | // http://www.cocoadev.com/index.pl?NSDataCategory 9 | // 10 | 11 | #import 12 | 13 | /*! Adds 32-bit cyclic redundancy check message to NSData. 14 | * Methods extracted from source given at 15 | * http://www.cocoadev.com/index.pl?NSDataCategory 16 | */ 17 | @interface NSData (CRC32) 18 | 19 | /*! 20 | * Calculates the CRC32 value of the receiver 21 | * 22 | * \return 32-bit CRC of the receiver. 23 | */ 24 | - (uint32_t)crc32; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Test/Unit/GITPackObject.rb: -------------------------------------------------------------------------------- 1 | describe 'GITPackObject' do 2 | before do 3 | simple_repository.repack 4 | @sha = GITObjectHash.objectHashWithString(simple_repository.commit('Initial commit').sha) 5 | @pth = simple_repository.pack_files.first 6 | @err = Pointer.new(:object) 7 | @pack = GITPackFile.packWithPath(@pth, error:@err) 8 | end 9 | 10 | describe '+packObjectWithData:type:' do 11 | before do 12 | @obj = @pack.unpackObjectWithSha1(@sha, error:@err) 13 | end 14 | should 'not be nil' do 15 | @obj.should.not.be.nil 16 | end 17 | should 'have type: GITObjectTypeCommit' do 18 | @obj.type.should == GITObjectTypeCommit 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+DeltaPatching.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+DeltaPatching.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 08/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /*! 13 | * This category provides delta patching support to enable the restoration of 14 | * delta compressed data. 15 | */ 16 | @interface NSData (DeltaPatching) 17 | 18 | //! \name Patching 19 | /*! 20 | * Returns the restored data from patching the \a deltaData with the receiver. 21 | * 22 | * \param deltaData NSData to patch the receiver with 23 | * \return restored data 24 | */ 25 | - (NSData *)dataByDeltaPatchingWithData: (NSData *)deltaData; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Source/GITActor+Parsing.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITActor+Parsing.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 10/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITActor+Parsing.h" 10 | #import "NSRangeEnd.h" 11 | 12 | 13 | @implementation GITActor (Parsing) 14 | 15 | + (GITActor *)actorWithParsedString: (NSString *)str { 16 | return [[[self alloc] initWithParsedString:str] autorelease]; 17 | } 18 | 19 | - (id)initWithParsedString: (NSString *)str { 20 | NSRange delimRange = [str rangeOfString:@" <"]; 21 | NSString *nameChunk = [str substringToIndex:delimRange.location]; 22 | NSString *emailChunk = [str substringFromIndex:NSRangeEnd(delimRange)]; 23 | return [self initWithName:nameChunk email:emailChunk]; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Test/Unit/GITBranch.rb: -------------------------------------------------------------------------------- 1 | describe "GITBranch +branchWithName:inRepo:" do 2 | before do 3 | @branch = GITBranch.branchWithName("master", inRepo:simple_repository.git_repo) 4 | end 5 | 6 | should "not be nil" do 7 | @branch.should.not.be.nil 8 | end 9 | should "have name 'master'" do 10 | @branch.name.should == 'master' 11 | end 12 | end 13 | 14 | describe "GITBranch +branchFromRef:inRepo:" do 15 | before do 16 | @ref = GITRef.refWithName("refs/heads/master", andTarget:"6c20014aaa67fc2ac4958f899b6d5494cb30331f", inRepo:simple_repository.git_repo) 17 | @branch = GITBranch.branchFromRef(@ref) 18 | end 19 | 20 | should "not be nil" do 21 | @branch.should.not.be.nil 22 | end 23 | should "have name 'master'" do 24 | @branch.name.should == 'master' 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Vendor/OmniGroup/OmniBase/NSError-OBExtensions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2005-2008 Omni Development, Inc. All rights reserved. 2 | // 3 | // This software may only be used and reproduced according to the 4 | // terms in the file OmniSourceLicense.html, which should be 5 | // distributed with this project and can also be found at 6 | // . 7 | // 8 | // $Id$ 9 | 10 | #import 11 | #import "OBError.h" 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | extern NSString * const OBUserCancelledActionErrorKey; 18 | 19 | @interface NSError (OBExtensions) 20 | 21 | - (BOOL)hasUnderlyingErrorDomain:(NSString *)domain code:(int)code; 22 | - (BOOL)causedByUserCancelling; 23 | 24 | - initWithPropertyList:(NSDictionary *)propertyList; 25 | - (NSDictionary *)toPropertyList; 26 | @end 27 | 28 | #if defined(__cplusplus) 29 | } // extern "C" 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /Test/Unit/GITPackCollection.rb: -------------------------------------------------------------------------------- 1 | describe 'GITPackCollection' do 2 | before do 3 | simple_repository.repack 4 | @err = Pointer.new(:object) 5 | @collection = GITPackCollection.collectionWithContentsOfDirectory(simple_repository.pack_path, error:@err) 6 | end 7 | should 'not be nil' do 8 | @collection.should.not.be.nil 9 | end 10 | should 'not have an error' do 11 | @err[0].should.be.nil 12 | end 13 | 14 | describe '-unpackObjectWithSha1:error:' do 15 | before do 16 | @sha = GITObjectHash.objectHashWithString(simple_repository.commit("Initial commit").sha) 17 | @obj = @collection.unpackObjectWithSha1(@sha, error:@err) 18 | end 19 | should 'not be nil' do 20 | @obj.should.not.be.nil 21 | end 22 | should 'not have an error' do 23 | @err[0].should.be.nil 24 | end 25 | should 'have found the correct object' do 26 | @obj.sha1.unpackedString.should == @sha.unpackedString 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /Resource/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleName 10 | ${PRODUCT_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | ${BUNDLE_ID_PREFIX:rfc1034Identifier}.${PRODUCT_NAME:rfc1034Identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | CFBundleShortVersionString 24 | 1.0 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Test/Unit/GITPackedRefsEnumerator.rb: -------------------------------------------------------------------------------- 1 | describe "GITPackedRefsEnumerator" do 2 | before do 3 | simple_repository.pack_refs 4 | @enumerator = GITPackedRefsEnumerator.enumeratorForRepo(simple_repository.git_repo) 5 | end 6 | 7 | should "not be nil" do 8 | @enumerator.should.not.be.nil 9 | end 10 | should "set packedRefsPath" do 11 | @enumerator.packedRefsPath.should.not.be.nil 12 | end 13 | should "set packedRefsPath to repo-root/packed-refs" do 14 | @enumerator.packedRefsPath.should == "#{simple_repository.root}/.git/packed-refs" 15 | end 16 | 17 | describe "-nextObject" do 18 | before do 19 | @object = @enumerator.nextObject 20 | end 21 | should "not be nil" do 22 | @object.should.not.be.nil 23 | end 24 | should "not start with #" do 25 | @object[0,1].should.not == '#' 26 | end 27 | should "have two parts" do 28 | sha1, name = @object.split(" ") 29 | sha1.length.should == 40 30 | name.should.not.be.nil 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /Test/Unit/NSData+DeltaPatching.rb: -------------------------------------------------------------------------------- 1 | describe "NSData(DeltaPatching)" do 2 | before do 3 | delta_string = [0x02, 0x06].pack('C*') # Header (source size: 2, target size: 6) 4 | delta_string << [0x01].pack('C*') # Take one byte from the delta 5 | delta_string << "F" 6 | delta_string << [0x90, 0x02].pack('C*') # Take 2 bytes from the source (offset: 0, length: 2) 7 | delta_string << [0x03].pack('C*') # Take three bytes from the delta 8 | delta_string << "bar" 9 | 10 | @data = "oo".to_data 11 | @delta_data = delta_string.to_data 12 | end 13 | 14 | should "delta patch data with patch" do 15 | patched_data = @data.dataByDeltaPatchingWithData(@delta_data) 16 | patched_data.should.not.be.nil 17 | NSString.alloc.initWithData(patched_data, encoding:NSASCIIStringEncoding).should == "Foobar" 18 | end 19 | 20 | should "raise an exception if source size does not match" do 21 | should.raise(Exception) do 22 | "o".to_data.dataByDeltaPatchingWithData(@delta_data) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /Source/Git.h: -------------------------------------------------------------------------------- 1 | // 2 | // Git.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 21/04/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | #pragma mark Core 13 | #import "GITRepo.h" 14 | #import "GITError.h" 15 | #import "GITObjectHash.h" 16 | 17 | #pragma mark Objects 18 | #import "GITObject.h" 19 | #import "GITCommit.h" 20 | #import "GITBlob.h" 21 | #import "GITTag.h" 22 | #import "GITTree.h" 23 | #import "GITTreeItem.h" 24 | 25 | #pragma mark Object Assistance 26 | #import "GITActor.h" 27 | #import "GITDateTime.h" 28 | 29 | #pragma mark Refs and Branches 30 | #import "GITBranch.h" 31 | #import "GITRef.h" 32 | #import "GITRefResolver.h" 33 | #import "GITPackedRefsEnumerator.h" 34 | 35 | #pragma mark Enumerators 36 | #import "GITCommitEnumerator.h" 37 | 38 | #pragma mark PACK Files and Indexes 39 | #import "GITPackFile.h" 40 | #import "GITPackIndex.h" 41 | #import "GITPackObject.h" 42 | 43 | #pragma mark Omni Error Extensions 44 | #import "NSError-OBExtensions.h" 45 | #import "OBError.h" 46 | -------------------------------------------------------------------------------- /Test/Unit/GITLooseObject.rb: -------------------------------------------------------------------------------- 1 | describe 'GITLooseObject' do 2 | before do 3 | @err = Pointer.new(:object) 4 | @sha = GITObjectHash.objectHashWithString(simple_repository.head.sha) 5 | @loose = GITLooseObject.looseObjectWithSha1(@sha, from:simple_repository.objects_path, error:@err) 6 | end 7 | should 'not be nil' do 8 | @loose.should.not.be.nil 9 | end 10 | should 'not have an error' do 11 | @err[0].should.be.nil 12 | end 13 | should 'have a type' do 14 | @loose.type.should == GITObjectTypeCommit 15 | end 16 | should 'have data' do 17 | @loose.data.should.not.be.nil 18 | end 19 | should 'have sha1' do 20 | @loose.sha1.unpackedString.should == @sha.unpackedString 21 | end 22 | 23 | describe '-objectInRepo:error:' do 24 | before do 25 | @repo = simple_repository.git_repo 26 | @commit = @loose.objectInRepo(@repo, error:@err) 27 | end 28 | should 'not be nil' do 29 | @commit.should.not.be.nil 30 | end 31 | should 'not have an error' do 32 | @err[0].should.be.nil 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /Source/GITPackFileWriterPlaceholder.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFileWriterPlaceholder.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackFileWriterPlaceholder.h" 10 | #import "GITPackFileWriterVersionTwo.h" 11 | #import "GITError.h" 12 | 13 | 14 | @implementation GITPackFileWriterPlaceholder 15 | 16 | - (id)initWithDefaultVersion { 17 | return [self initWithVersion:2 error:NULL]; 18 | } 19 | 20 | - (id)initWithVersion: (NSUInteger)version error: (NSError **)error { 21 | NSZone *z = [self zone]; [self release]; 22 | NSString *errDesc; 23 | 24 | switch ( version ) { 25 | case 2: 26 | return [[GITPackFileWriterVersionTwo allocWithZone:z] init]; 27 | default: 28 | errDesc = [NSString stringWithFormat:NSLocalizedString(@"PACK file writer version %u unsupported", @"GITPackFileWriterErrorVersionUnsupported"), version]; 29 | GITError(error, GITPackFileWriterErrorVersionUnsupported, errDesc); 30 | return nil; 31 | } 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Source/GITPackFileVersionTwo.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFileVersionTwo.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackFile.h" 10 | 11 | 12 | typedef enum { 13 | GITPackFileDeltaTypeOfs = 6, 14 | GITPackFileDeltaTypeRefs = 7, 15 | } GITPackFileDeltaType; 16 | 17 | typedef struct { 18 | int type; 19 | off_t offset; 20 | size_t size; 21 | } GITPackFileObjectHeader; 22 | 23 | extern const uint8_t const GITPackFileVersionTwoVersionBytes[]; 24 | 25 | @class GITPackIndex; 26 | @interface GITPackFileVersionTwo : GITPackFile { 27 | NSData *data; 28 | GITPackIndex *index; 29 | } 30 | 31 | @property (copy) NSData *data; 32 | @property (retain) GITPackIndex *index; 33 | 34 | - (GITPackObject *)unpackObjectAtOffset: (off_t)offset sha1: (GITObjectHash *)objectHash error: (NSError **)error; 35 | - (GITPackObject *)unpackDeltaPackedObjectAtOffset: (off_t)offset objectHeader: (GITPackFileObjectHeader *)header sha1: (GITObjectHash *)objectHash error: (NSError **)error; 36 | 37 | - (NSRange)checksumRange; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+Searching.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Searching.m 3 | // 4 | // Created by Geoffrey Garside on 17/07/2008. 5 | // Copyright 2008 Geoffrey Garside. All rights reserved. 6 | // 7 | 8 | #import "NSData+Searching.h" 9 | #import // memchr 10 | 11 | @implementation NSData (Searching) 12 | 13 | - (NSRange)rangeFrom:(NSInteger)start toByte:(NSInteger)c; 14 | { 15 | const char *pdata = [self bytes]; 16 | NSUInteger len = [self length]; 17 | if (start < len) { 18 | char *end = memchr(pdata + start, c, len - start); 19 | if (end != NULL) 20 | return NSMakeRange (start, end - (pdata + start)); 21 | } 22 | return NSMakeRange(NSNotFound, 0); 23 | } 24 | 25 | - (NSRange)rangeOfNullTerminatedBytesFrom:(NSInteger)start 26 | { 27 | return [self rangeFrom:start toByte:0x00]; 28 | } 29 | 30 | - (NSData*)subdataFromIndex:(NSUInteger)anIndex 31 | { 32 | NSRange theRange = NSMakeRange(anIndex, [self length] - anIndex); 33 | return [self subdataWithRange:theRange]; 34 | } 35 | - (NSData*)subdataToIndex:(NSUInteger)anIndex 36 | { 37 | NSRange theRange = NSMakeRange(0, anIndex); 38 | return [self subdataWithRange:theRange]; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Test/Unit/GITTreeItem.rb: -------------------------------------------------------------------------------- 1 | describe 'GITTreeItem' do 2 | before do 3 | @sha1 = GITObjectHash.objectHashWithString('afc72b2679ebc4d72b08ffe66d1adfb556d96f79') 4 | @tree = GITTree.alloc.init 5 | @treeItem = GITTreeItem.itemInTree(@tree, withMode:0x100644, name:".gitignore", sha1:@sha1) 6 | end 7 | describe '-hash' do 8 | should 'be equal to @sha1.hash' do 9 | @treeItem.hash.should == @sha1.hash 10 | end 11 | end 12 | describe '-isEqual:' do 13 | before do 14 | err = Pointer.new(:object) 15 | repo = simple_repository.git_repo 16 | @sha2 = GITObjectHash.objectHashWithString('a2d0bba4b0d4f9bf0168dd68d1588bde05aa3e5b') 17 | @blob = repo.objectWithSha1(@sha2, error:err) 18 | 19 | @treeItem2 = GITTreeItem.itemInTree(@tree, withMode:0x100644, name:"file.txt", sha1:@sha2) 20 | end 21 | should 'be equal to itself' do 22 | @treeItem.isEqual(@treeItem).should.be.true 23 | end 24 | should 'not be equal to object with different sha1' do 25 | @treeItem.isEqual(@treeItem2).should.be.false 26 | end 27 | should 'be equal to object with matching sha1' do 28 | @treeItem2.isEqual(@blob).should.be.true 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /Source/GITBlob.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITBlob.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 13/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITBlob.h" 10 | #import "GITObjectHash.h" 11 | #import "NSData+SHA1.h" 12 | 13 | 14 | @implementation GITBlob 15 | 16 | @synthesize content; 17 | 18 | + (NSString *)typeName { 19 | return [NSString stringWithString:@"blob"]; 20 | } 21 | 22 | + (GITObjectType)type { 23 | return GITObjectTypeBlob; 24 | } 25 | 26 | + (GITBlob *)blobFromData: (NSData *)data sha1: (GITObjectHash *)objectHash repo: (GITRepo *)repo error: (NSError **)error { 27 | return [[[self alloc] initFromData:data sha1:objectHash repo:repo error:error] autorelease]; 28 | } 29 | 30 | - (id)initFromData: (NSData *)data sha1: (GITObjectHash *)objectHash repo: (GITRepo *)theRepo error: (NSError **)error { 31 | if ( ![super initWithType:GITObjectTypeBlob sha1:objectHash repo:theRepo] ) 32 | return nil; 33 | 34 | self.content = data; 35 | self.size = [data length]; 36 | 37 | return self; 38 | } 39 | 40 | - (void)dealloc { 41 | self.content = nil; 42 | [super dealloc]; 43 | } 44 | 45 | - (NSData *)rawContent { 46 | return self.content; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriterPlaceholder.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriterPlaceholder.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexWriterPlaceholder.h" 10 | #import "GITPackIndexWriterVersionOne.h" 11 | #import "GITPackIndexWriterVersionTwo.h" 12 | #import "GITError.h" 13 | 14 | 15 | @implementation GITPackIndexWriterPlaceholder 16 | 17 | - (id)initWithDefaultVersion { 18 | return [self initWithVersion:2 error:NULL]; 19 | } 20 | 21 | - (id)initWithVersion: (NSUInteger)version error: (NSError **)error { 22 | NSZone *z = [self zone]; [self release]; 23 | NSString *errDesc; 24 | 25 | switch ( version ) { 26 | case 1: 27 | return [[GITPackIndexWriterVersionOne allocWithZone:z] init]; 28 | case 2: 29 | return [[GITPackIndexWriterVersionTwo allocWithZone:z] init]; 30 | default: 31 | errDesc = [NSString stringWithFormat:NSLocalizedString(@"PACK Index writer version %u unsupported", @"GITPackIndexWriterErrorVersionUnsupported"), version]; 32 | GITError(error, GITPackIndexWriterErrorVersionUnsupported, errDesc); 33 | return nil; 34 | } 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Vendor/OmniGroup/OmniBase/OBBacktraceBuffer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2008 Omni Development, Inc. All rights reserved. 2 | // 3 | // This software may only be used and reproduced according to the 4 | // terms in the file OmniSourceLicense.html, which should be 5 | // distributed with this project and can also be found at 6 | // . 7 | // 8 | // $Id$ 9 | 10 | /* 11 | This is an internal header for OmniBase and OmniCrashCatcher to communicate information about the backtrace buffer. Other code shouldn't need to see it. 12 | */ 13 | 14 | #define OBBacktraceBufferAddressCount 16 15 | #define OBBacktraceBufferTraceCount 8 16 | 17 | enum OBBacktraceBufferType { 18 | OBBacktraceBuffer_Unused = 0, 19 | OBBacktraceBuffer_Allocated = 1, 20 | OBBacktraceBuffer_OBAssertionFailure = 2 21 | }; 22 | 23 | struct OBBacktraceBuffer { 24 | volatile int type; 25 | uintptr_t context; 26 | void *frames[OBBacktraceBufferAddressCount]; 27 | }; 28 | 29 | #define OBBacktraceBufferInfoVersionMagic 2 30 | struct OBBacktraceBufferInfo { 31 | unsigned char version; 32 | unsigned char infoSize; 33 | unsigned char addressesPerTrace; 34 | unsigned char traceCount; 35 | uintptr_t backtraces; 36 | uintptr_t nextTrace; 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriter+Shared.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriter+Shared.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 06/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexWriter+Shared.h" 10 | #import "GITPackIndexWriterObject.h" 11 | #import "GITObjectHash.h" 12 | 13 | 14 | @implementation GITPackIndexWriter (Shared) 15 | 16 | #pragma mark Silencers 17 | - (uint32_t *)fanoutTable { 18 | [self doesNotRecognizeSelector: _cmd]; 19 | return NULL; 20 | } 21 | - (NSInteger)stream: (NSOutputStream *)stream write: (uint8_t *)bytes maxLength: (NSUInteger)length { 22 | [self doesNotRecognizeSelector: _cmd]; 23 | return -1; 24 | } 25 | 26 | #pragma mark Fanout Table Methods 27 | - (void)addObjectHashToFanoutTable: (GITObjectHash *)sha1 { 28 | uint8_t byte = [sha1 firstPackedByte]; 29 | [self fanoutTable][byte] += 1; 30 | } 31 | 32 | - (NSInteger)writeFanoutTableToStream: (NSOutputStream *)stream { 33 | int i; 34 | uint32_t current = 0, out; 35 | 36 | for ( i = 0; i < 256; i++ ) { 37 | current += [self fanoutTable][i]; 38 | out = CFSwapInt32HostToBig(current); 39 | if ( [self stream:stream write:(uint8_t *)&out maxLength:4] < 0 ) { 40 | return -1; 41 | } 42 | } 43 | 44 | return 0; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Source/GITPackObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackObject.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackObject.h" 10 | #import "NSData+DeltaPatching.h" 11 | 12 | 13 | @implementation GITPackObject 14 | 15 | @synthesize data, type, sha1; 16 | 17 | + (GITPackObject *)packObjectWithData: (NSData *)packData sha1: (GITObjectHash *)objectHash type: (GITObjectType)objectType { 18 | return [[[self alloc] initWithData:packData sha1:objectHash type:objectType] autorelease]; 19 | } 20 | 21 | - (id)initWithData: (NSData *)packData sha1: (GITObjectHash *)objectHash type: (GITObjectType)objectType { 22 | if ( ![super init] ) 23 | return nil; 24 | 25 | self.data = packData; 26 | self.type = objectType; 27 | self.sha1 = objectHash; 28 | 29 | return self; 30 | } 31 | 32 | - (void)dealloc { 33 | self.type = 0; 34 | self.data = nil; 35 | self.sha1 = nil; 36 | [super dealloc]; 37 | } 38 | 39 | - (id)packObjectByDeltaPatchingWithData: (NSData *)deltaData { 40 | return [[self class] packObjectWithData:[self.data dataByDeltaPatchingWithData:deltaData] sha1:self.sha1 type:self.type]; 41 | } 42 | 43 | - (NSUInteger)length { 44 | return [self.data length]; 45 | } 46 | 47 | - (GITObject *)objectInRepo: (GITRepo *)repo error: (NSError **)error { 48 | return [GITObject objectOfType:self.type withData:self.data sha1:self.sha1 repo:repo error:error]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Source/GITBlob.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITBlob.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 13/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITObject.h" 11 | 12 | 13 | /*! 14 | * This class represents \e Blob objects in a git repository. 15 | * 16 | * Blob objects store the data which makes up the files of a repository, 17 | * these objects have no name associated with them and are only identified 18 | * by their content. 19 | */ 20 | @interface GITBlob : GITObject { 21 | NSData *content; //!< object contents 22 | } 23 | 24 | //! \name Properties 25 | @property (retain) NSData *content; 26 | 27 | //! \name Creating Blobs 28 | /*! 29 | * Creates and returns a blob from the \a data. 30 | * 31 | * The blob copies the \a data as its content and initialises the \a repo as its parent. 32 | * The \a error parameter will contain a description of any errors which occur though 33 | * at the moment there aren't any cases when an error would be raised when creating a 34 | * blob object. 35 | * 36 | * \param data The data to create the receiver with 37 | * \param objectHash The SHA1 hash of the receiver 38 | * \param repo The repository the receiver is a member of 39 | * \param error NSError describing the error if one occurs 40 | * \return A blob object 41 | */ 42 | + (GITBlob *)blobFromData: (NSData *)data sha1: (GITObjectHash *)objectHash repo: (GITRepo *)repo error: (NSError **)error; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Source/GITBranch.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITBranch.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 17/10/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITBranch.h" 10 | #import "GITRepo.h" 11 | #import "GITRef.h" 12 | #import "GITRefResolver.h" 13 | #import "GITCommit.h" 14 | 15 | 16 | @implementation GITBranch 17 | 18 | @synthesize repo, ref, remote; 19 | 20 | + (GITBranch *)branchWithName: (NSString *)theName inRepo: (GITRepo *)theRepo { 21 | GITRef *branchRef = [[theRepo refResolver] resolveRefWithName:theName]; 22 | if ( !branchRef ) 23 | return nil; 24 | return [[self class] branchFromRef:branchRef]; 25 | } 26 | 27 | + (GITBranch *)branchFromRef: (GITRef *)theRef { 28 | return [[[[self class] alloc] initFromRef:theRef] autorelease]; 29 | } 30 | 31 | - (id)initFromRef: (GITRef *)theRef { 32 | if ( ![super init] ) 33 | return nil; 34 | 35 | self.repo = [theRef repo]; 36 | self.ref = theRef; 37 | self.remote = [[self.ref name] rangeOfString:@"remotes"].location != NSNotFound; 38 | 39 | return self; 40 | } 41 | 42 | - (NSString *)name { 43 | if ( remote ) 44 | return [[self.ref name] stringByReplacingOccurrencesOfString:@"refs/remotes/" withString:@""]; 45 | else 46 | return [[self.ref name] stringByReplacingOccurrencesOfString:@"refs/heads/" withString:@""]; 47 | } 48 | 49 | - (void)dealloc { 50 | self.repo = nil; 51 | self.ref = nil; 52 | [super dealloc]; 53 | } 54 | 55 | - (GITCommit *)head { 56 | return (GITCommit *)[self.ref target]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Source/GITActor.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITActor.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 10/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITActor.h" 10 | 11 | 12 | @implementation GITActor 13 | 14 | @synthesize name, email; 15 | 16 | + (NSString *)defaultName { 17 | return @"User"; 18 | } 19 | 20 | + (NSString *)defaultEmail { 21 | NSProcessInfo *pi = [NSProcessInfo processInfo]; 22 | NSDictionary *env = [pi environment]; 23 | return [NSString stringWithFormat:@"%@@%@", [env objectForKey:@"USER"], [pi hostName]]; 24 | } 25 | 26 | + (GITActor *)actor { 27 | return [[[self alloc] initWithName:[self defaultName] email:[self defaultEmail]] autorelease]; 28 | } 29 | 30 | + (GITActor *)actorWithName: (NSString *)name { 31 | return [[[self alloc] initWithName:name] autorelease]; 32 | } 33 | 34 | + (GITActor *)actorWithName: (NSString *)name email: (NSString *)email { 35 | return [[[self alloc] initWithName:name email:email] autorelease]; 36 | } 37 | 38 | - (id)initWithName: (NSString *)theName { 39 | NSString *theEmail = [[self class] defaultEmail]; 40 | return [self initWithName:theName email:theEmail]; 41 | } 42 | 43 | - (id)initWithName: (NSString *)theName email: (NSString *)theEmail { 44 | if ( ![super init] ) 45 | return nil; 46 | 47 | self.name = theName; 48 | self.email = theEmail; 49 | 50 | return self; 51 | } 52 | 53 | - (void)dealloc { 54 | self.name = nil; 55 | self.email = nil; 56 | [super dealloc]; 57 | } 58 | 59 | - (NSString *)description { 60 | return [NSString stringWithFormat:@"%@ <%@>", self.name, self.email]; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Test/TestHelper.rb: -------------------------------------------------------------------------------- 1 | require 'time' 2 | 3 | Dir[File.expand_path("../Support/*.rb", __FILE__)].each do |support_file| 4 | require support_file 5 | end 6 | 7 | class Bacon::Context 8 | include Git::Helpers 9 | end 10 | 11 | def default_repository 12 | GITRepo.repoWithRoot(TEST_REPO) 13 | end 14 | 15 | class String 16 | def to_data 17 | return NSData.data unless length > 0 18 | 19 | bytes = self.bytes.to_a 20 | p = Pointer.new_with_type("char *", bytes.length) 21 | 22 | bytes.each_with_index do |char, i| 23 | p[i] = char 24 | end 25 | 26 | NSData.dataWithBytes(p, length:bytes.length) 27 | end 28 | end 29 | 30 | class Time 31 | def to_git 32 | GITDateTime.dateTimeWithTimestamp(to_i, timeZoneOffset:rfc2822.split(" ").last) 33 | end 34 | end 35 | 36 | class NSData 37 | def ===(rhs) 38 | raise ArgumentError, "Must be kind of NSData" unless rhs.kind_of?(NSData) 39 | self.isEqualToData(rhs) ? true : false 40 | end 41 | end 42 | 43 | class NSDate 44 | def ===(rhs) 45 | raise ArgumentError, "Must be kind of NSDate" unless rhs.kind_of?(NSDate) 46 | self.isEqualToDate(rhs) ? true : false 47 | end 48 | end 49 | 50 | class NSString 51 | def ===(rhs) 52 | raise ArgumentError, "Must be kind of NSString" unless rhs.kind_of?(NSString) 53 | self.isEqualToString(rhs) ? true : false 54 | end 55 | end 56 | 57 | class NSTimeZone 58 | def ===(rhs) 59 | raise ArgumentError, "Must be kind of NSTimeZone" unless rhs.kind_of?(NSTimeZone) 60 | self.isEqualToTimeZone(rhs) ? true : false 61 | end 62 | end 63 | 64 | class GITDateTime 65 | def to_time 66 | Time.parse(stringWithFormat("yyyy-MM-dd HH:mm:ss ZZZ")) 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /Source/GITRef.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITRef.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 20/09/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITRef.h" 10 | #import "GITRepo.h" 11 | #import "GITRefResolver.h" 12 | #import "GITObjectHash.h" 13 | 14 | 15 | @implementation GITRef 16 | 17 | @synthesize repo, name, link, targetName; 18 | 19 | + (id)refWithName: (NSString *)theName andTarget: (NSString *)theTarget inRepo: (GITRepo *)theRepo { 20 | return [[[GITRef alloc] initWithName:theName andTarget:theTarget inRepo:theRepo] autorelease]; 21 | } 22 | 23 | - (id)initWithName:(NSString *)theName andTarget: (NSString *)theTarget inRepo: (GITRepo *)theRepo { 24 | if ( ![super init] ) 25 | return nil; 26 | 27 | self.repo = theRepo; 28 | self.name = theName; 29 | self.targetName = theTarget; 30 | self.link = NO; 31 | 32 | if ( [self.targetName hasPrefix:@"ref: "] ) 33 | self.link = YES; 34 | 35 | return self; 36 | } 37 | 38 | - (void)dealloc { 39 | self.repo = nil; 40 | self.name = nil; 41 | self.targetName = nil; 42 | [super dealloc]; 43 | } 44 | 45 | - (GITObjectHash *)targetObjectHash { 46 | return [GITObjectHash objectHashWithString:targetName]; 47 | } 48 | 49 | - (GITRef *)resolve { 50 | if ( !link ) { 51 | return self; 52 | } else { 53 | return [[[self.repo refResolver] resolveRefWithName:[self.targetName stringByReplacingOccurrencesOfString:@"ref: " withString:@""]] resolve]; 54 | } 55 | } 56 | 57 | - (GITObject *)target { 58 | GITRef *end = [self resolve]; 59 | return [self.repo objectWithSha1:[end targetObjectHash] error:NULL]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Source/GITActor+Parsing.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITActor+Parsing.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 10/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITActor.h" 11 | 12 | 13 | /*! 14 | * This category provides additional initialisers for GITActor. 15 | * 16 | * These initialisers aid in creating GITActor objects from the kind of 17 | * content typically retrieved by the GITObject(Parsing) category methods. 18 | */ 19 | @interface GITActor (Parsing) 20 | 21 | //! \name Creating and Initialising Actors from Parsed Strings 22 | /*! 23 | * Creates and returns an actor object by parsing the name and email from 24 | * a string contained in a GITCommit header (author or committer) line. 25 | * 26 | * This method expects pre-processed input of the form: "[name] <[email]". 27 | * example: "E. L. Gato " 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /Source/GITPackedRefsEnumerator.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackedRefsEnumerator.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 04/10/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class NSData, NSString, GITRepo; 12 | 13 | /*! 14 | * The \c GITPackedRefsEnumerator class provides enumeration 15 | * of \c packed-refs contents. 16 | * 17 | * This enumerator allows for iteration over the contents of 18 | * \c packed-refs using either normal enumeration 19 | * 20 | \verbatim 21 | id ref; 22 | GITRepo *repo = [GITRepo repo]; 23 | GITPackedRefsEnumerator *e = [GITPackedRefsEnumerator enumeratorForRepo:repo]; 24 | 25 | while ( (object = [e nextObject]) ) { 26 | // do something with object... 27 | } 28 | \endverbatim 29 | * 30 | * or using Fast Enumeration 31 | * 32 | \verbatim 33 | GITRepo *repo = [GITRepo repo]; 34 | 35 | for ( id object in [GITPackedRefsEnumerator enumeratorForRepo:repo]) { 36 | // do something with object... 37 | } 38 | \endverbatim 39 | */ 40 | @interface GITPackedRefsEnumerator : NSEnumerator { 41 | NSString *packedRefsPath; 42 | BOOL hasPackedRefs; 43 | BOOL started; 44 | 45 | NSData *packedRefs; 46 | NSRange currentRange; 47 | } 48 | 49 | //! \name Creating and Initialising Packed Refs Enumerator 50 | /*! 51 | * Returns a packed refs enumerator for the provided repository. 52 | * 53 | * \param theRepo Repository to create the enumerator for 54 | * \return packed refs enumerator 55 | * \sa initWithRepo: 56 | */ 57 | + (GITPackedRefsEnumerator *)enumeratorForRepo: (GITRepo *)theRepo; 58 | 59 | /*! 60 | * Returns a packed refs enumerator for the provided repository. 61 | * 62 | * \param theRepo Repository to create the enumerator for 63 | * \return packed refs enumerator 64 | */ 65 | - (id)initWithRepo: (GITRepo *)theRepo; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Test/Unit/GITTag.rb: -------------------------------------------------------------------------------- 1 | require 'zlib' 2 | 3 | class Bacon::Context 4 | def tagDataForSha(sha) 5 | file = "#{simple_repository.root}/.git/objects/%s/%s" % [sha[0,2], sha[2..-1]] 6 | Zlib::Inflate.inflate(File.read(file)).split("\x00", 2)[1].to_data 7 | end 8 | def tagForSha(sha) 9 | d = tagDataForSha(sha) 10 | GITTag.tagFromData(d, sha1:GITObjectHash.objectHashWithString(sha), repo:@repo, error:@err) 11 | end 12 | end 13 | 14 | describe 'GITTag' do 15 | before do 16 | @err = Pointer.new(:object) 17 | @repo = simple_repository.git_repo 18 | @info = simple_repository.tags['v0.0.0'] 19 | @tag = tagForSha(@info.sha) 20 | @date = @info.date.to_git 21 | end 22 | should 'not be nil' do 23 | @tag.should.not.be.nil 24 | end 25 | should 'not have an error' do 26 | @err[0].should.be.nil 27 | end 28 | should 'belong to repo' do 29 | @tag.repo.should == @repo 30 | end 31 | should 'reference object' do 32 | @tag.targetSha1.unpackedString.should == @info.ref 33 | end 34 | should 'have name v0.0.0' do 35 | @tag.name.should == 'v0.0.0' 36 | end 37 | should 'have cachedData' do 38 | @tag.cachedData.should.not.be.nil 39 | end 40 | should 'have tagger "Geoff Garside"' do 41 | @tag.tagger.name.should == @info.tagger_name 42 | end 43 | should 'have tagger email "geoff@geoffgarside.co.uk"' do 44 | @tag.tagger.email.should == @info.tagger_email 45 | end 46 | should 'have author date' do 47 | @tag.taggerDate.date.should === @date.date 48 | @tag.taggerDate.timeZone.should === @date.timeZone 49 | end 50 | should 'have message' do 51 | @tag.message.should == "v0.0.0" 52 | end 53 | describe '-rawContent' do 54 | before do 55 | @data = tagDataForSha(@tag.sha1.unpackedString) 56 | end 57 | should 'return formatted tag data' do 58 | @tag.rawContent.should === @data 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriterObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriterObject.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 06/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexWriterObject.h" 10 | #import "GITObjectHash.h" 11 | 12 | 13 | @implementation GITPackIndexWriterObject 14 | @synthesize sha1, offset, crc32; 15 | 16 | + (GITPackIndexWriterObject *)indexWriterObjectWithName: (GITObjectHash *)sha1 atOffset: (NSUInteger)offset { 17 | return [[[self alloc] initWithName:sha1 atOffset:offset] autorelease]; 18 | } 19 | 20 | - (id)initWithName: (GITObjectHash *)theSha1 atOffset: (NSUInteger)theOffset { 21 | if ( ![super init] ) 22 | return nil; 23 | 24 | self.sha1 = theSha1; 25 | self.offset = theOffset; 26 | 27 | return self; 28 | } 29 | 30 | - (void)dealloc { 31 | self.sha1 = nil; 32 | [super dealloc]; 33 | } 34 | 35 | - (NSUInteger)hash { 36 | return [self.sha1 hash]; 37 | } 38 | - (BOOL)isEqual:(id)other { 39 | if ( !other ) return NO; // other is nil 40 | if ( other == self ) return YES; // pointers match? 41 | 42 | if ( [other isKindOfClass:[self class]]) // Same class? 43 | return [self isEqualToIndexWriterObject:other]; 44 | if ( [other isKindOfClass:[GITObjectHash class]] ) // An object hash? 45 | return [self isEqualToObjectHash:other]; 46 | 47 | return NO; // Definitely not then 48 | } 49 | - (BOOL)isEqualToIndexWriterObject: (GITPackIndexWriterObject *)rhs { 50 | return [self.sha1 isEqualToObjectHash:rhs.sha1]; 51 | } 52 | - (BOOL)isEqualToObjectHash: (GITObjectHash *)rhs { 53 | return [self.sha1 isEqualToObjectHash:rhs]; 54 | } 55 | - (NSComparisonResult)compare: (GITPackIndexWriterObject *)obj { 56 | return [self.sha1 compare:obj.sha1]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+Searching.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+Searching.h 3 | // 4 | // Created by Geoffrey Garside on 17/07/2008. 5 | // Copyright 2008 Geoffrey Garside. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | /*! 11 | * This category provides ranging and splitting of NSData objects. 12 | */ 13 | @interface NSData (Searching) 14 | 15 | //! \name Getting ranges of NSData 16 | /*! Returns the range of bytes up to the first occurrence of byte c from start. 17 | * \return Range of bytes up to the first occurrence of c from start. If c 18 | * can not be found then the NSRange.location will be set to NSNotFound. 19 | */ 20 | - (NSRange)rangeFrom:(NSInteger)start toByte:(NSInteger)c; 21 | 22 | /*! Returns the range of bytes up to the first NULL byte from start. 23 | * \return Range of bytes up to the first NULL from start. If no NULL 24 | * can be found then the NSRange.location will be set to NSNotFound. 25 | */ 26 | - (NSRange)rangeOfNullTerminatedBytesFrom:(NSInteger)start; 27 | 28 | //! \name Splitting up NSData 29 | /*! Returns a data object containing a copy of the receiver's bytes 30 | * that fall within the limits specified by a given index and the 31 | * end of the bytes. 32 | * \param index Start of the range which defines the limits to extract. 33 | * \return A data object containing a copy of the receiver's bytes 34 | * that fall within the limits of index and the end of the bytes. 35 | * \see -subdataWithRange: 36 | */ 37 | - (NSData*)subdataFromIndex:(NSUInteger)index; 38 | 39 | /*! Returns a data object containing a copy of the receiver's bytes 40 | * that fall within the first byte and index. 41 | * \param index End of the range which defines the limits to extract. 42 | * \return A data object containing a copy of the receiver's bytes 43 | * that fall within the first byte and index. 44 | * \see -subdataWithRange: 45 | */ 46 | - (NSData*)subdataToIndex:(NSUInteger)index; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Source/Category/NSTimeZone/NSTimeZone+Offset.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimeZone+Offset.h 3 | // 4 | // Created by Geoffrey Garside on 28/07/2008. 5 | // Copyright (c) 2008 Geoff Garside 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | 26 | #import 27 | #import 28 | 29 | @interface NSTimeZone (Offset) 30 | 31 | /*! Creates and returns a time zone with the specified offset. 32 | * The string is broken down into the hour and minute components 33 | * which are then used to work out the number of seconds from GMT. 34 | * \param offset The timezone offset as a string such as "+0100" 35 | * \return A time zone with the specified offset 36 | * \see +timeZoneForSecondsFromGMT: 37 | */ 38 | + (id)timeZoneWithStringOffset:(NSString*)offset; 39 | 40 | /*! Returns the receivers offset as an HHMM formatted string. 41 | * \return The receivers offset as a string in HHMM format. 42 | */ 43 | - (NSString*)offsetString; 44 | @end 45 | -------------------------------------------------------------------------------- /Source/GITPackIndexVersionTwo.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexVersionTwo.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndex.h" 10 | 11 | 12 | extern const uint8_t const GITPackIndexVersionTwoVersionBytes[]; 13 | 14 | @interface GITPackIndexVersionTwo : GITPackIndex { 15 | NSData *data; 16 | NSArray *fanoutTable; 17 | } 18 | 19 | @property (copy) NSData *data; 20 | @property (copy) NSArray *fanoutTable; 21 | 22 | /*! 23 | * Returns the index of the SHA1 contained in \a packedSha. 24 | * 25 | * \author Brian Chapados 26 | * \param packedSha NSData containing the packed SHA1 of the object to return the index of 27 | * \return index of the SHA1 in \a packedSha, or \c NSNotFound if not found 28 | * \sa indexOfSha1: 29 | */ 30 | - (NSUInteger)indexOfPackedSha1: (NSData *)packedSha; 31 | 32 | /*! 33 | * Returns the offset value from the position specified in the extended offset table at the specified \a idx. 34 | * 35 | * \param idx Index position of the offset to retrieve 36 | * \return the offset at the specified index position 37 | * \sa packOffsetAtIndex: 38 | * \sa packOffsetForSha1:error: 39 | * \sa packOffsetForSha1: 40 | */ 41 | - (off_t)extendedPackOffsetAtOffset: (uint32_t)idx; 42 | 43 | /*! 44 | * Returns the offset in the PACK file of the object \a packedSha. 45 | * 46 | * \param packedSha SHA1 of the object to get the offset of 47 | * \param error NSError describing the error which occurred 48 | * \return offset in the PACK of the object \a packedSha or NSNotFound if an error occurred 49 | * \sa packOffsetForSha1:error: 50 | * \sa packOffsetForSha1: 51 | * \sa packOffsetAtIndex: 52 | */ 53 | - (off_t)packOffsetForPackedSha1: (NSData *)packedSha error: (NSError **)error; 54 | 55 | - (NSRange)fanoutTableRange; 56 | - (NSRange)shaTableRange; 57 | - (NSRange)crcTableRange; 58 | - (NSRange)offsetTableRange; 59 | - (NSRange)extendedOffsetTableRange; 60 | - (NSRange)packChecksumRange; 61 | - (NSRange)indexChecksumRange; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Test/Unit/GITBlob.rb: -------------------------------------------------------------------------------- 1 | require 'digest/sha1' 2 | 3 | BLOB_CONTENTS = < 12 | 13 | /*! Adds compression and decompression messages to NSData. 14 | * Methods extracted from source given at 15 | * http://www.cocoadev.com/index.pl?NSDataCategory 16 | */ 17 | @interface NSData (Compression) 18 | 19 | #pragma mark - 20 | #pragma mark Zlib Compression routines 21 | //! \name Zlib Compression and Decompression 22 | /*! Returns a data object containing a Zlib decompressed copy of the receivers contents. 23 | * \returns A data object containing a Zlib decompressed copy of the receivers contents. 24 | */ 25 | - (NSData *) zlibInflate; 26 | /*! Returns a data object containing a Zlib compressed copy of the receivers contents. 27 | * \returns A data object containing a Zlib compressed copy of the receivers contents. 28 | */ 29 | - (NSData *) zlibDeflate; 30 | 31 | /*! 32 | * Inflates the data into a given buffer starting at a certain offset. 33 | * 34 | * \param buffer A pointer to an instance of NSMutableData to popluated with the inflated data. 35 | * \param offset The offset at which to start reading from [self bytes] 36 | * \return The number of bytes consumed 37 | * \internal 38 | * This method is not part of the original cocoadev.com source 39 | */ 40 | - (int) zlibInflateInto: (NSMutableData *)buffer offset:(NSUInteger) offset; 41 | 42 | #pragma mark - 43 | #pragma mark Gzip Compression routines 44 | //! \name GZip Compression and Decompression 45 | /*! Returns a data object containing a Gzip decompressed copy of the receivers contents. 46 | * \returns A data object containing a Gzip decompressed copy of the receivers contents. 47 | */ 48 | - (NSData *) gzipInflate; 49 | /*! Returns a data object containing a Gzip compressed copy of the receivers contents. 50 | * \returns A data object containing a Gzip compressed copy of the receivers contents. 51 | */ 52 | - (NSData *) gzipDeflate; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Source/GITDateTime.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITDateTime.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 10/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITDateTime.h" 10 | #import "NSTimeZone+Offset.h" 11 | 12 | 13 | @implementation GITDateTime 14 | 15 | @synthesize date, timeZone; 16 | 17 | + (GITDateTime *)dateTimeWithDate: (NSDate *)date { 18 | return [[[self alloc] initWithDate:date] autorelease]; 19 | } 20 | 21 | + (GITDateTime *)dateTimeWithDate: (NSDate *)date timeZone: (NSTimeZone *)timeZone { 22 | return [[[self alloc] initWithDate:date timeZone:timeZone] autorelease]; 23 | } 24 | 25 | + (GITDateTime *)dateTimeWithTimestamp: (NSTimeInterval)seconds timeZoneOffset: (NSString *)offset { 26 | return [[[self alloc] initWithTimestamp:seconds timeZoneOffset:offset] autorelease]; 27 | } 28 | 29 | - (id)initWithDate: (NSDate *)theDate { 30 | return [self initWithDate:theDate timeZone:[NSTimeZone defaultTimeZone]]; 31 | } 32 | 33 | - (id)initWithDate: (NSDate *)theDate timeZone: (NSTimeZone *)theTimeZone { 34 | if ( ![super init] ) 35 | return nil; 36 | 37 | self.date = theDate; 38 | self.timeZone = theTimeZone; 39 | 40 | return self; 41 | } 42 | 43 | - (id)initWithTimestamp: (NSTimeInterval)seconds timeZoneOffset: (NSString *)offset { 44 | return [self initWithDate:[NSDate dateWithTimeIntervalSince1970:seconds] 45 | timeZone:[NSTimeZone timeZoneWithStringOffset:offset]]; 46 | } 47 | 48 | - (void)dealloc { 49 | self.date = nil; 50 | self.timeZone = nil; 51 | [super dealloc]; 52 | } 53 | 54 | - (NSString *)stringWithFormat: (NSString *)format { 55 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 56 | [dateFormatter setTimeZone:timeZone]; 57 | [dateFormatter setDateFormat:format]; 58 | 59 | NSString *str = [dateFormatter stringFromDate:date]; 60 | [dateFormatter release]; 61 | 62 | return str; 63 | } 64 | 65 | - (NSString *)description { 66 | return [NSString stringWithFormat:@"%.0f %@", 67 | [self.date timeIntervalSince1970], [self.timeZone offsetString]]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Source/GITPackIndexVersionOne.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexVersionOne.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndex.h" 10 | 11 | 12 | @interface GITPackIndexVersionOne : GITPackIndex { 13 | NSData *data; 14 | NSArray *fanoutTable; 15 | } 16 | 17 | @property (copy) NSData *data; 18 | @property (copy) NSArray *fanoutTable; 19 | 20 | /*! 21 | * Parses the fanout table. 22 | * 23 | * This method iterates over the 256 entries in the fan out table and gathers the 24 | * number of packed SHA1 hashes which start with a byte that is less than or equal 25 | * to the fanout index value. 26 | * 27 | * If the offset fanout table is corrupted a GITPackIndexErrorCorrupt NSError will 28 | * be returned. 29 | * 30 | * \todo Examine memory implications of this method 31 | * \param error NSError describing the error which occurred 32 | * \return YES or NO indicating if the offsets were successfully parsed 33 | * \sa offsets 34 | * \sa offsets: 35 | */ 36 | - (BOOL)parseFanoutTable: (NSError **)error; 37 | 38 | /*! 39 | * Returns the index in the index table of the SHA1 \a packedSha. 40 | * 41 | * \author Brian Chapados 42 | * \param packedSha NSData containing the packed SHA1 of the object to return the index of 43 | * \return index of the SHA1 in \a packedSha, or \c NSNotFound if not found 44 | * \sa indexOfSha1: 45 | */ 46 | - (NSUInteger)indexOfPackedSha1: (NSData *)packedSha; 47 | 48 | /*! 49 | * Returns the offset in the PACK file of the object \a packedSha. 50 | * 51 | * \param packedSha SHA1 of the object to get the offset of 52 | * \param error NSError describing the error which occurred 53 | * \return offset in the PACK of the object \a packedSha or NSNotFound if an error occurred 54 | * \sa packOffsetForSha1:error: 55 | * \sa packOffsetForSha1: 56 | * \sa packOffsetAtIndex: 57 | */ 58 | - (off_t)packOffsetForPackedSha1: (NSData *)packedSha error: (NSError **)error; 59 | 60 | - (NSRange)fanoutTableRange; 61 | - (NSRange)indexTableRange; 62 | - (NSRange)packChecksumRange; 63 | - (NSRange)indexChecksumRange; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Source/GITBranch.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITBranch.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 17/10/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITRepo, GITRef, GITCommit; 13 | 14 | /*! 15 | * This class represents \c GITBranch objects. 16 | * 17 | * Branches are threads of commits in a repository, they refer to the 18 | * HEAD of a thread. 19 | */ 20 | @interface GITBranch : NSObject { 21 | GITRepo *repo; //!< Repository the branch is part of 22 | GITRef *ref; //!< Ref the branch points to 23 | BOOL remote; //!< Flag indicating if the branch is a remote branch 24 | } 25 | 26 | //! \name Properties 27 | @property (retain) GITRepo *repo; 28 | @property (retain) GITRef *ref; 29 | @property (assign,getter=isRemote) BOOL remote; 30 | 31 | //! \name Creating and Initialising Branches 32 | /*! 33 | * Creates and returns a branch by the name specified. 34 | * 35 | * The name of the branch is used to resolve the reference for the branch, 36 | * the resolved reference is used to create the GITBranch. 37 | * 38 | * \param theName Name of the branch 39 | * \param theRepo Repository to resolve the branch in 40 | * \return branch initialised with the name specified, or nil if the branch can't be resolved 41 | * \sa branchFromRef: 42 | * \sa initFromRef: 43 | */ 44 | + (GITBranch *)branchWithName: (NSString *)theName inRepo: (GITRepo *)theRepo; 45 | 46 | /*! 47 | * Creates and returns an autoreleased branch using the reference specified. 48 | * 49 | * \param theRef GITRef describing the branch 50 | * \return branch initialised with the reference 51 | * \sa initFromRef: 52 | */ 53 | + (GITBranch *)branchFromRef: (GITRef *)theRef; 54 | 55 | /*! 56 | * Creates and returns a branch for the specified reference. 57 | * 58 | * \param theRef GITRef describing the branch 59 | * \return branch initialised with the reference 60 | */ 61 | - (id)initFromRef: (GITRef *)theRef; 62 | 63 | //! \name Branch Information 64 | /*! 65 | * Returns the name of the branch. 66 | * 67 | * \return name of the branch 68 | */ 69 | - (NSString *)name; 70 | 71 | /*! 72 | * Returns the commit which represents the HEAD of the receiver. 73 | * 74 | * \return Commit HEAD of the receiver 75 | */ 76 | - (GITCommit *)head; 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+SHA1.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+SHA1.h 3 | // 4 | // Created by Geoffrey Garside on 29/06/2008. 5 | // Copyright (c) 2008 Geoff Garside 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | // Based on, believed to be public domain, source code available at 26 | // http://www.cocoadev.com/index.pl?NSDataCategory, though modified 27 | // to use the CommonCrypto API available on Mac OS and iPhone platforms. 28 | // 29 | 30 | #import 31 | 32 | /*! 33 | * The category provides SHA1 hashing of NSData objects. 34 | * 35 | * The hashing is implemented using the CommonCrypto API which is 36 | * available on both the Mac OS and iPhone OS platforms. It is very 37 | * similar to the OpenSSL implementation, the only real difference 38 | * being that the CommonCrypto macros are prefixed with \c CC_. 39 | */ 40 | @interface NSData (SHA1) 41 | 42 | //! \name Hashing 43 | /*! Returns the SHA1 digest of the receivers contents. 44 | * \return SHA1 digest of the receivers contents. 45 | */ 46 | - (NSData*) sha1Digest; 47 | 48 | /*! Returns a string with the SHA1 digest of the receivers contents. 49 | * \return String with the SHA1 digest of the receivers contents. 50 | */ 51 | - (NSString*) sha1DigestString; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Source/GITTree.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITTree.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 13/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITObject.h" 11 | 12 | 13 | @class GITRepo; 14 | 15 | /*! 16 | * This class represents \e Tree objects in a git repository. 17 | * 18 | * Tree objects store the file system structure of a repository at the 19 | * point at which the tree is created. They represent directories or 20 | * folders and contain references to items in the directory/folder which 21 | * may be other trees, directory/folder, or blobs, files. 22 | * 23 | * Tree objects store a list of directory items with their associated 24 | * modes and SHA1 references. The SHA1 references are in the packed form. 25 | * \verbatim 26 | * 100644 .gitignore a5cc2925ca8258af241be7e5b0381edf30266302 27 | * 100644 README 69e27356ef629022720d868ab0c0e3394775b6c1 28 | * \endverbatim 29 | */ 30 | @interface GITTree : GITObject { 31 | NSArray *items; //!< The items in the tree 32 | } 33 | 34 | //! \name Properties 35 | @property (copy) NSArray *items; 36 | 37 | //! \name Creating and Initialising Trees 38 | /*! 39 | * Creates and returns a tree from the \a data. 40 | * 41 | * The \a data is parsed to extract the references to the tree contents, the 42 | * contents are then stored in the \c items array as GITTreeItem objects. 43 | * 44 | * \param data The data to create the tree from 45 | * \param objectHash The SHA1 hash of the receiver 46 | * \param repo The repository the tree is a member of 47 | * \param error NSError describing any errors which occurred 48 | * \return A tree object 49 | */ 50 | + (GITTree *)treeFromData: (NSData *)data sha1: (GITObjectHash *)objectHash repo: (GITRepo *)repo error: (NSError **)error; 51 | 52 | //! \name Querying a Tree 53 | /*! 54 | * Returns a Boolean value that indicates whether a given object is present in the tree. 55 | * 56 | * This method determines whether anObject is present in the array by sending an isEqual: 57 | * message to each of the array’s objects (and passing anObject as the parameter to each isEqual: message). 58 | * 59 | * \param anObject An object 60 | * \return \c YES if \a anObject is present in the tree, otherwise \c NO. 61 | */ 62 | - (BOOL)containsObject: (id)anObject; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Source/GITActor.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITActor.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 10/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /*! 13 | * This class describes people who have made a contribution to a repository. 14 | * 15 | * People are committers and authors, they have names and e-mail addresses, 16 | * this class collects these two pieces of information. 17 | */ 18 | @interface GITActor : NSObject { 19 | NSString *name; //!< Name of the actor 20 | NSString *email; //!< Email address of the actor 21 | } 22 | 23 | //! \name Properties 24 | @property (copy) NSString *name; 25 | @property (copy) NSString *email; 26 | 27 | //! \name Creating and Initialising Actors 28 | /*! 29 | * Creates and returns an unknown actor. 30 | * 31 | * The name by default is "User" and the email address is derived from the 32 | * current process environment \c USER and \c HOST variables. 33 | * 34 | * \return unknown actor 35 | * \sa actorWithName:email: 36 | */ 37 | + (GITActor *)actor; 38 | 39 | /*! 40 | * Creates and returns an actor with the \a name. 41 | * 42 | * The email address is derived from the current process environment \c USER 43 | * and \c HOST variables. 44 | * 45 | * \param name Name of the actor 46 | * \return actor with \a name 47 | * \sa initWithName: 48 | */ 49 | + (GITActor *)actorWithName: (NSString *)name; 50 | 51 | /*! 52 | * Creates and returns an actor with the \a name and \a email. 53 | * 54 | * \param name Name of the actor 55 | * \param email Email address of the actor 56 | * \return actor with \a name and \a email 57 | * \sa initWithName:email: 58 | */ 59 | + (GITActor *)actorWithName: (NSString *)name email: (NSString *)email; 60 | 61 | /*! 62 | * Creates and returns an actor with the \a name. 63 | * 64 | * The email address is derived from the current process environment \c USER 65 | * and \c HOST variables. 66 | * 67 | * \param name Name of the actor 68 | * \return actor with \a name 69 | * \sa initWithName:email: 70 | */ 71 | - (id)initWithName: (NSString *)name; 72 | 73 | /*! 74 | * Creates and returns an actor with the \a name and \a email. 75 | * 76 | * \param name Name of the actor 77 | * \param email Email address of the actor 78 | * \return actor with \a name and \a email 79 | */ 80 | - (id)initWithName: (NSString *)name email: (NSString *)email; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Test/Unit/GITTree.rb: -------------------------------------------------------------------------------- 1 | require 'zlib' 2 | 3 | class Bacon::Context 4 | def treeDataForSha(sha) 5 | file = "#{simple_repository.root}/.git/objects/%s/%s" % [sha[0,2], sha[2..-1]] 6 | Zlib::Inflate.inflate(File.read(file)).split("\x00", 2)[1].to_data 7 | end 8 | def treeForSha(sha) 9 | d = treeDataForSha(sha) 10 | GITTree.treeFromData(d, sha1:GITObjectHash.objectHashWithString(sha), repo:@repo, error:@err) 11 | end 12 | end 13 | 14 | describe 'GITTree' do 15 | before do 16 | @err = Pointer.new(:object) 17 | @repo = simple_repository.git_repo 18 | @tree = treeForSha('227c6c88ba35e67a1341a068c07d1c1639d6582e') 19 | end 20 | 21 | should 'not be nil' do 22 | @tree.should.not.be.nil 23 | end 24 | should 'not have an error' do 25 | @err[0].should.be.nil 26 | end 27 | should 'belong to repo' do 28 | @tree.repo.should == @repo 29 | end 30 | should 'have items' do 31 | @tree.items.should.not.be.empty 32 | end 33 | should 'have 2 items' do 34 | @tree.items.count.should == 2 35 | end 36 | 37 | describe 'items[0]' do 38 | before do 39 | @item = @tree.items[0] 40 | end 41 | should 'not be nil' do 42 | @item.should.not.be.nil 43 | end 44 | should 'belong to a tree' do 45 | @item.parent.should == @tree 46 | end 47 | should 'have mode' do 48 | @item.mode.should == 0x100644 49 | end 50 | should 'have name' do 51 | @item.name.should == '.gitignore' 52 | end 53 | should 'have sha1' do 54 | @item.sha1.unpackedString.should == 'afc72b2679ebc4d72b08ffe66d1adfb556d96f79' 55 | end 56 | end 57 | 58 | describe 'items[1]' do 59 | before do 60 | @item = @tree.items[1] 61 | end 62 | should 'not be nil' do 63 | @item.should.not.be.nil 64 | end 65 | should 'belong to a tree' do 66 | @item.parent.should == @tree 67 | end 68 | should 'have mode' do 69 | @item.mode.should == 0x100644 70 | end 71 | should 'have name' do 72 | @item.name.should == 'testfile.txt' 73 | end 74 | should 'have sha1' do 75 | @item.sha1.unpackedString.should == 'bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea' 76 | end 77 | end 78 | 79 | describe '-rawContent' do 80 | before do 81 | @data = treeDataForSha(@tree.sha1.unpackedString) 82 | end 83 | should 'return data in the correct format' do 84 | @tree.rawContent.should === @data 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriterVersionOne.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriterVersionOne.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITPackIndexWriter.h" 11 | 12 | 13 | @interface GITPackIndexWriterVersionOne : GITPackIndexWriter { 14 | char state; //!< Internal state of the writer 15 | CC_SHA1_CTX ctx; //!< CommonCrypto checksum context 16 | uint32_t fanoutTable[256]; //!< Fanout table 17 | NSMutableArray *objects; //!< Indexed objects 18 | NSInteger objectsWritten; //!< Count of objects written 19 | NSData *packChecksum; //!< PACK file checksum 20 | } 21 | 22 | /*! 23 | * Returns the fanout table array 24 | * 25 | * \return fanout table array 26 | */ 27 | - (uint32_t *)fanoutTable; 28 | 29 | //! \name Output stream writers 30 | /*! 31 | * Writes and checksums the buffer into the stream. 32 | * 33 | * \param stream Output stream to write to 34 | * \param buffer Bytes to write to the stream 35 | * \param length Number of bytes to be written to the stream 36 | * \return number of bytes written 37 | * \sa stream:writeData: 38 | */ 39 | - (NSInteger)stream: (NSOutputStream *)stream write: (const uint8_t *)buffer maxLength: (NSUInteger)length; 40 | 41 | /*! 42 | * Writes and checksums the data into the stream. 43 | * 44 | * \param stream Output stream to write to 45 | * \param data Data to write to the stream 46 | * \return number of bytes written 47 | * \sa stream:write:maxLength: 48 | */ 49 | - (NSInteger)stream: (NSOutputStream *)stream writeData: (NSData *)data; 50 | 51 | /*! 52 | * Writes the checksum of the written data into the stream. 53 | * 54 | * \param stream Output stream to write the checksum to 55 | * \return number of bytes written 56 | * \sa stream:writeData: 57 | */ 58 | - (NSInteger)writeChecksumToStream: (NSOutputStream *)stream; 59 | 60 | /*! 61 | * Writes the PACK object entry to the stream. 62 | * 63 | * \param stream Output stream to write the object entry to 64 | * \return number of bytes written 65 | */ 66 | - (NSInteger)writeObjectEntryToStream: (NSOutputStream *)stream; 67 | 68 | /*! 69 | * Writes the PACK checksum to the output stream. 70 | * 71 | * \param stream Output stream to write the PACK checksum to 72 | * \return number of bytes written 73 | */ 74 | - (NSInteger)writePackChecksumToStream: (NSOutputStream *)stream; 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /Test/Unit/GITPackIndex.rb: -------------------------------------------------------------------------------- 1 | describe "GITPackIndexVersionOne" do 2 | before do 3 | simple_repository.repack 4 | @idx_info = simple_repository.v1_indexes.first 5 | @pth = @idx_info.file 6 | @err = Pointer.new(:object) 7 | @idx = GITPackIndex.packIndexWithPath(@pth, error:@err) 8 | end 9 | 10 | should "not be nil" do 11 | @idx.should.not.be.nil 12 | end 13 | should "not have an error" do 14 | @err[0].should.be.nil 15 | end 16 | should "be version 1" do 17 | @idx.version.should == 1 18 | end 19 | should "parse the fanout table" do 20 | @idx.fanoutTable(@err).should.not.be.nil 21 | @err[0].should.be.nil 22 | end 23 | should "find index of blob object bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea in index" do 24 | hash = GITObjectHash.objectHashWithString("bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea") 25 | index = @idx.indexOfSha1(hash) 26 | index.should.not == NSNotFound 27 | end 28 | should "find a pack offset value for bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea" do 29 | hash = GITObjectHash.objectHashWithString("bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea") 30 | offset = @idx.packOffsetForSha1(hash) 31 | offset.should == @idx_info["bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea"] 32 | end 33 | end 34 | 35 | describe "GITPackIndexVersionTwo" do 36 | before do 37 | simple_repository.repack 38 | @idx_info = simple_repository.indexes.first 39 | @pth = simple_repository.index_files.first 40 | @err = Pointer.new(:object) 41 | @idx = GITPackIndex.packIndexWithPath(@pth, error:@err) 42 | end 43 | 44 | should "not be nil" do 45 | @idx.should.not.be.nil 46 | end 47 | should "not have an error" do 48 | @err[0].should.be.nil 49 | end 50 | should "be version 2" do 51 | @idx.version.should == 2 52 | end 53 | should "parse the fanout table" do 54 | @idx.fanoutTable(@err).should.not.be.nil 55 | @err[0].should.be.nil 56 | end 57 | should "find index of blob object bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea in index" do 58 | hash = GITObjectHash.objectHashWithString("bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea") 59 | index = @idx.indexOfSha1(hash) 60 | index.should.not == NSNotFound 61 | end 62 | should "find a pack offset value for bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea" do 63 | hash = GITObjectHash.objectHashWithString("bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea") 64 | offset = @idx.packOffsetForSha1(hash) 65 | offset.should == @idx_info['bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea'] 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /Source/Category/NSTimeZone/NSTimeZone+Offset.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimeZone+Offset.m 3 | // 4 | // Created by Geoffrey Garside on 28/07/2008. 5 | // Copyright (c) 2008 Geoff Garside 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | 26 | #import "NSTimeZone+Offset.h" 27 | 28 | static const unsigned short int HourInSeconds = 3600; 29 | static const unsigned short int MinuteInSeconds = 60; 30 | 31 | @implementation NSTimeZone (Offset) 32 | 33 | + (id)timeZoneWithStringOffset:(NSString*)offset 34 | { 35 | NSString * hours = [offset substringWithRange:NSMakeRange(1, 2)]; 36 | NSString * mins = [offset substringWithRange:NSMakeRange(3, 2)]; 37 | 38 | NSTimeInterval seconds = ([hours integerValue] * HourInSeconds) + ([mins integerValue] * MinuteInSeconds); 39 | if ([offset characterAtIndex:0] == '-') 40 | seconds = seconds * -1; 41 | 42 | return [self timeZoneForSecondsFromGMT:seconds]; 43 | } 44 | - (NSString*)offsetString 45 | { 46 | BOOL negative = NO; 47 | unsigned short int hours, mins; //!< Shouldn't ever be > 60 48 | 49 | NSTimeInterval seconds = [self secondsFromGMT]; 50 | if (seconds < 0) { 51 | negative = YES; 52 | seconds = seconds * -1; 53 | } 54 | 55 | hours = (NSInteger)seconds / HourInSeconds; 56 | mins = ((NSInteger)seconds % HourInSeconds) / MinuteInSeconds; 57 | 58 | return [NSString stringWithFormat:@"%c%02d%02d", 59 | negative ? '-' : '+', hours, mins]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Source/GITPackIndexPlaceholder.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexPlaceholder.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexPlaceholder.h" 10 | #import "GITPackIndexVersionOne.h" 11 | #import "GITPackIndexVersionTwo.h" 12 | #import "GITError.h" 13 | 14 | 15 | @implementation GITPackIndexPlaceholder 16 | 17 | - (id)initWithPath: (NSString *)indexPath error: (NSError **)error { 18 | BOOL isDirectory = NO; 19 | 20 | if ( ![[NSFileManager defaultManager] fileExistsAtPath:indexPath isDirectory:&isDirectory] ) { 21 | GITError(error, GITPackIndexErrorPathNotFound, NSLocalizedString(@"Path to the PACK Index file did not exist", @"GITPackIndexErrorPathNotFound")); 22 | return nil; 23 | } 24 | 25 | if ( isDirectory ) { 26 | GITError(error, GITPackIndexErrorPathIsDirectory, NSLocalizedString(@"Path to the PACK Index file was a directory", @"GITPackIndexErrorPathIsDirectory")); 27 | return nil; 28 | } 29 | 30 | NSData *indexData = [NSData dataWithContentsOfFile:indexPath options:NSDataReadingMapped error:error]; 31 | if ( !indexData ) 32 | return nil; 33 | 34 | return [self initWithData:indexData error:error]; 35 | } 36 | 37 | - (id)initWithData: (NSData *)indexData error: (NSError **)error { 38 | if ( !indexData ) 39 | return nil; // No error explanation, seems a bit silly to say "Oi!! indexData was nil." 40 | 41 | uint32_t num, *n = # // to read file signature and version into 42 | NSZone *z = [self zone]; [self release]; 43 | 44 | // Extract possible magic number bytes 45 | memset(n, 0x0, 4); 46 | [indexData getBytes:n range:NSMakeRange(0, 4)]; 47 | 48 | if ( memcmp(n, GITPackIndexVersionDiscriminator, 4) != 0 ) { 49 | return [[GITPackIndexVersionOne allocWithZone:z] initWithData:indexData error:error]; 50 | } 51 | 52 | // Extract version for v2 and higher index files 53 | memset(n, 0x0, 4); 54 | [indexData getBytes:n range:NSMakeRange(4, 4)]; 55 | 56 | // Version 2? 57 | if ( memcmp(n, GITPackIndexVersionTwoVersionBytes, 4) == 0 ) { // Version 2 Index file 58 | return [[GITPackIndexVersionTwo allocWithZone:z] initWithData:indexData error:error]; 59 | } 60 | 61 | // Raise error as version not supported 62 | NSString *desc = [NSString stringWithFormat:NSLocalizedString(@"PACK Index version %u unsupported", @"GITPackIndexErrorVersionUnsupported"), CFSwapInt32BigToHost(num)]; 63 | GITError(error, GITPackIndexErrorVersionUnsupported, desc); 64 | return nil; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Test/Unit/GITPackFile.rb: -------------------------------------------------------------------------------- 1 | describe "GITPackFileVersionTwo" do 2 | before do 3 | simple_repository.repack 4 | @pth = simple_repository.pack_files.first 5 | @err = Pointer.new(:object) 6 | @pack = GITPackFile.packWithPath(@pth, error:@err) 7 | end 8 | 9 | should 'not be nil' do 10 | @pack.should.not.be.nil 11 | end 12 | 13 | should 'not have an error' do 14 | @err[0].should.be.nil 15 | end 16 | 17 | should 'be version 2' do 18 | @pack.version.should == 2 19 | end 20 | 21 | should 'have an index' do 22 | @pack.index.should.not.be.nil 23 | @pack.index.should.be.kind_of GITPackIndex 24 | end 25 | 26 | should 'have some objects' do 27 | @pack.numberOfObjects.should.be > 0 28 | end 29 | 30 | describe '-unpackObjectWithSha1:error:' do 31 | before do 32 | @sha = GITObjectHash.objectHashWithString(simple_repository.commit('Initial commit').sha) 33 | @obj = @pack.unpackObjectWithSha1(@sha, error:@err) 34 | end 35 | should 'not be nil' do 36 | @obj.should.not.be.nil 37 | end 38 | should 'not have an error' do 39 | @err[0].should.be.nil 40 | end 41 | should 'be kind of GITPackObject' do 42 | @obj.should.be.kind_of GITPackObject 43 | end 44 | end 45 | end 46 | 47 | describe "GITPackFileVersionTwo with delta base offsets" do 48 | before do 49 | @packed_repo = repository "Packed" do 50 | commit "Initial Commit" do 51 | write "a.txt", "a" * 4096 52 | end 53 | commit "Subsequent Commit" do 54 | write "a.txt", "a" * 4096 + "foo" 55 | end 56 | end 57 | 58 | @packed_repo.repack 59 | 60 | @pth = @packed_repo.pack_files.first 61 | @err = Pointer.new(:object) 62 | @pack = GITPackFile.packWithPath(@pth, error:@err) 63 | end 64 | 65 | describe 'unpacking diff packed object' do 66 | before do 67 | @sha = GITObjectHash.objectHashWithString('9d235ed07cd19811a6ceb342de82f190e49c9f68') 68 | @obj = @pack.unpackObjectWithSha1(@sha, error:@err) 69 | end 70 | 71 | should 'not be nil' do 72 | @obj.should.not.be.nil 73 | end 74 | 75 | should 'have correct data' do 76 | @obj.data.to_str.should == 'a' * 4096 77 | end 78 | end 79 | 80 | describe 'unpacking first object' do 81 | before do 82 | @sha = GITObjectHash.objectHashWithString('3de87ec8610b04fb16f9cdd605ceb685b9bcbb8b') 83 | @obj = @pack.unpackObjectWithSha1(@sha, error:@err) 84 | end 85 | 86 | should 'not be nil' do 87 | @obj.should.not.be.nil 88 | end 89 | 90 | should 'have correct data' do 91 | @obj.data.to_str.should == "a" * 4096 + "foo" 92 | end 93 | end 94 | end -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+DeltaPatching.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+DeltaPatching.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 08/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NSData+DeltaPatching.h" 11 | 12 | 13 | @implementation NSData (DeltaPatching) 14 | 15 | - (size_t)deltaPatchingPatchHeaderSize: (const uint8_t **)bytes_p { 16 | const uint8_t *bytes = *bytes_p; 17 | size_t size = 0; 18 | int shift = 0; 19 | 20 | do { 21 | size |= ((*bytes & 0x7f) << shift); 22 | *bytes_p += 1; 23 | shift += 7; 24 | } while ( (*bytes++ & 0x80) != 0 ); 25 | 26 | return size; 27 | } 28 | 29 | - (NSData *)dataByDeltaPatchingWithData: (NSData *)delta { 30 | const uint8_t *deltaBytes = [delta bytes]; 31 | const uint8_t *endOfDelta = deltaBytes + [delta length]; 32 | 33 | size_t sourceSize = [self deltaPatchingPatchHeaderSize:&deltaBytes]; //!< Get source size 34 | if ( sourceSize != [self length] ) 35 | [NSException raise:@"NSDataDeltaPatchingException" format:@"Delta Patch data is invalid"]; 36 | 37 | size_t targetSize = [self deltaPatchingPatchHeaderSize:&deltaBytes]; //!< Get target size 38 | NSMutableData *target = [NSMutableData dataWithCapacity:targetSize]; 39 | 40 | uint8_t c; 41 | size_t cp_off, cp_size; 42 | 43 | while ( deltaBytes < endOfDelta ) { 44 | c = *(deltaBytes++); 45 | 46 | if ( (c & 0x80) != 0 ) { 47 | 48 | cp_off = 0; /* Calculate the offset */ 49 | if ( 0 != (c & 0x01) ) cp_off = *(deltaBytes++); 50 | if ( 0 != (c & 0x02) ) cp_off |= *(deltaBytes++) << 8; 51 | if ( 0 != (c & 0x04) ) cp_off |= *(deltaBytes++) << 16; 52 | if ( 0 != (c & 0x08) ) cp_off |= *(deltaBytes++) << 24; 53 | 54 | cp_size = 0; /* Calculate the size */ 55 | if ( 0 != (c & 0x10) ) cp_size = *(deltaBytes++); 56 | if ( 0 != (c & 0x20) ) cp_size |= *(deltaBytes++) << 8; 57 | if ( 0 != (c & 0x40) ) cp_size |= *(deltaBytes++) << 16; 58 | if ( cp_size == 0 ) cp_size = 0x10000; 59 | 60 | [target appendData:[self subdataWithRange:NSMakeRange(cp_off, cp_size)]]; 61 | } else if ( c != 0 ) { 62 | [target appendBytes:deltaBytes length:c]; 63 | deltaBytes += c; 64 | } else { 65 | [NSException raise:@"NSDataDeltaPatchingException" format:@"Delta Patch data is invalid"]; 66 | return nil; //!< Exception should mean this isn't reached, I think 67 | } 68 | } 69 | 70 | return [[target copy] autorelease]; 71 | } 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /Source/GITError.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITError.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 15/09/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | // We use the __git_error and __git_error_domain macros to 9 | // make it easier to enter and update the error codes in 10 | // the project. If you define them here with the macro then 11 | // you can copy/paste the same code into GITErrors.m and 12 | // then add the value argument to the end of them. 13 | // 14 | 15 | #import 16 | 17 | extern NSString *GITErrorDomain; 18 | 19 | #import "NSError-OBExtensions.h" 20 | #import "OBError.h" 21 | 22 | // Define GITError* macros to use the OmniBase _OBError helper functions. If we decide to move away from OmniBase code, we can just redfine these. 23 | #define GITError(error, code, description) _OBError(error, GITErrorDomain, code, __FILE__, __LINE__, NSLocalizedDescriptionKey, description, nil) 24 | #define GITErrorWithInfo(error, code, ...) _OBError(error, GITErrorDomain, code, __FILE__, __LINE__, ## __VA_ARGS__) 25 | 26 | #define NSLocalizedStringWithArguments(format, comment, ...) [NSString stringWithFormat:NSLocalizedString(format, comment), ## __VA_ARGS__] 27 | 28 | enum { 29 | #pragma mark GITRepo Errors 30 | GITRepoErrorRootDoesNotExist, 31 | GITRepoErrorRootNotAccessible, 32 | GITRepoErrorRootInsane, 33 | GITRepoErrorSkeletonExists, 34 | GITRepoErrorSkeletonCreationFailed, 35 | 36 | #pragma mark GITRefResolver Errors 37 | GITRefResolverErrorRefNotFound, 38 | 39 | #pragma mark GITPackFile Errors 40 | GITPackFileErrorPathNotFound, 41 | GITPackFileErrorPathIsDirectory, 42 | GITPackFileErrorFileIsInvalid, 43 | GITPackFileErrorVersionUnsupported, 44 | GITPackFileErrorIndexMissing, 45 | GITPackFileErrorObjectTypeUnknown, 46 | GITPackFileErrorObjectSizeMismatch, 47 | GITPackFileErrorInflationFailed, 48 | 49 | #pragma mark GITPackIndex Errors 50 | GITPackIndexErrorPathNotFound, 51 | GITPackIndexErrorPathIsDirectory, 52 | GITPackIndexErrorVersionUnsupported, 53 | GITPackIndexErrorCorrupt, 54 | 55 | #pragma mark GITPackFile & GITPackIndex Errors 56 | GITPackErrorObjectNotFound, 57 | 58 | #pragma mark GITPackCollection Errors 59 | GITPackCollectionErrorDirectoryDoesNotExist, 60 | 61 | #pragma mark GITLooseObject Errors 62 | GITLooseObjectErrorDirectoryDoesNotExist, 63 | GITLooseObjectErrorObjectNotFound, 64 | 65 | #pragma mark GITObject Parsing Errors 66 | GITObjectErrorParsingFailed, 67 | 68 | #pragma mark GITPackFileWriter Errors 69 | GITPackFileWriterErrorVersionUnsupported, 70 | 71 | #pragma mark GITPackIndexWriter Errors 72 | GITPackIndexWriterErrorVersionUnsupported, 73 | 74 | }; 75 | -------------------------------------------------------------------------------- /Source/GITRef.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITRef.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 20/09/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITRepo, GITObject; 13 | 14 | /*! 15 | * \e GITRef objects provide access to git references. 16 | */ 17 | @interface GITRef : NSObject { 18 | GITRepo *repo; 19 | 20 | BOOL link; 21 | NSString *name; 22 | NSString *targetName; 23 | } 24 | 25 | @property (retain) GITRepo *repo; 26 | 27 | //! \name Properties 28 | /*! 29 | * Name of the reference. 30 | */ 31 | @property (copy) NSString *name; 32 | 33 | /*! 34 | * Flag indicating if the reference is a link to another reference or not. 35 | * 36 | * The accessor for this property is \c isLink 37 | */ 38 | @property (assign, getter=isLink) BOOL link; 39 | 40 | /*! 41 | * Value of the reference. 42 | * 43 | * If the receiver is a link then \c targetName contains the name of the 44 | * reference it points to. If the receiver is not a link then \c targetName 45 | * contains the SHA1 hash of the GITObject the reference points to. 46 | */ 47 | @property (copy) NSString *targetName; 48 | 49 | //! \name Creating and Initialising References 50 | /*! 51 | * Creates an autoreleased reference with the specified name, target and repository. 52 | * 53 | * \param theName Name of the reference 54 | * \param theTarget Link target or SHA1 hash of object 55 | * \param theRepo Repository the reference exists in 56 | * \return reference with the specified name, target and repository 57 | * \sa initWithName:andTarget:inRepo: 58 | */ 59 | + (id)refWithName: (NSString *)theName andTarget: (NSString *)theTarget inRepo: (GITRepo *)theRepo; 60 | 61 | /*! 62 | * Initialises a reference with the specified name, target and repository 63 | * 64 | * \param theName Name of the reference 65 | * \param theTarget Link target or SHA1 hash of object 66 | * \param theRepo Repository the reference exists in 67 | * \return reference with the specified name, target and repository 68 | */ 69 | - (id)initWithName: (NSString *)theName andTarget: (NSString *)theTarget inRepo: (GITRepo *)theRepo; 70 | 71 | //! \name Resolving Links 72 | /*! 73 | * Returns the reference linked to by the receiver, returns the receiver if not a link. 74 | * 75 | * The links will be resolved until the first non-link ref is found. 76 | * 77 | * \return reference linked to by the receiver, returns the receiver if not a link 78 | */ 79 | - (GITRef *)resolve; 80 | 81 | /*! 82 | * Returns the object referenced by resolving the receiver. 83 | * 84 | * \return Object the resolved receiver references 85 | * \sa resolve 86 | */ 87 | - (GITObject *)target; 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /Source/GITPackFilePlaceholder.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFilePlaceholder.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackFilePlaceholder.h" 10 | #import "GITPackFileVersionTwo.h" 11 | #import "GITError.h" 12 | 13 | 14 | @implementation GITPackFilePlaceholder 15 | 16 | - (id)initWithPath: (NSString *)packPath error: (NSError **)error { 17 | BOOL isDirectory = NO; 18 | 19 | if ( ![[NSFileManager defaultManager] fileExistsAtPath:packPath isDirectory:&isDirectory] ) { 20 | GITError(error, GITPackFileErrorPathNotFound, NSLocalizedString(@"Path to the PACK file did not exist", @"GITPackFileErrorPathNotFound")); 21 | return nil; 22 | } 23 | 24 | if ( isDirectory ) { 25 | GITError(error, GITPackFileErrorPathIsDirectory, NSLocalizedString(@"Path to the PACK file was a directory", @"GITPackFileErrorPathIsDirectory")); 26 | return nil; 27 | } 28 | 29 | NSData *packData = [NSData dataWithContentsOfFile:packPath options:NSDataReadingMapped error:error]; 30 | if ( !packData ) 31 | return nil; 32 | 33 | return [self initWithData:packData indexPath:[[packPath stringByDeletingPathExtension] stringByAppendingPathExtension:@"idx"] error:error]; 34 | } 35 | 36 | - (id)initWithData: (NSData *)packData indexPath: (NSString *)indexPath error: (NSError **)error { 37 | if ( !packData ) 38 | return nil; // No error explanation, seems a bit silly to say "Oi!! packData was nil." 39 | 40 | uint32_t num, *n = # // to read file signature and version into 41 | NSZone *z = [self zone]; [self release]; 42 | 43 | // Extract signature bytes 44 | memset(n, 0x0, 4); 45 | [packData getBytes:n range:NSMakeRange(0, 4)]; 46 | 47 | if ( memcmp(n, GITPackFileSignature, 4) != 0 ) { // signature doesn't match, not a pack file 48 | GITError(error, GITPackFileErrorFileIsInvalid, NSLocalizedString(@"PACK Data is missing signature", @"GITPackFileErrorFileIsInvalid")); 49 | return nil; 50 | } 51 | 52 | // Extract version bytes 53 | memset(n, 0x0, 4); 54 | [packData getBytes:n range:NSMakeRange(4, 4)]; 55 | 56 | // Initialise version specific class 57 | if ( memcmp(n, GITPackFileVersionTwoVersionBytes, 4) == 0 ) { // Version 2 PACK file 58 | return [[GITPackFileVersionTwo allocWithZone:z] initWithData:packData indexPath:indexPath error:error]; 59 | } 60 | 61 | // Raise error as version not supported 62 | NSString *desc = [NSString stringWithFormat:NSLocalizedString(@"PACK file version %u unsupported", @"GITPackFileErrorVersionUnsupported"), CFSwapInt32BigToHost(num)]; 63 | GITError(error, GITPackFileErrorVersionUnsupported, desc); 64 | return nil; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Test/Unit/GITRef.rb: -------------------------------------------------------------------------------- 1 | # Test creation of a GITRef, these do not read from the file system 2 | # under normal circumstances, the GITRefResolver reads the file system. 3 | # The GITRef might be given the ability to write refs to the file system. 4 | describe "GITRef +refWithName:andTarget:inRepo:" do 5 | describe "ref HEAD" do 6 | before do 7 | @ref = GITRef.refWithName("HEAD", andTarget:"ref: refs/heads/master", inRepo:simple_repository.git_repo) 8 | end 9 | 10 | should "not be nil" do 11 | @ref.should.not.be.nil 12 | end 13 | should "be a link" do 14 | @ref.should.be.link 15 | end 16 | should "have name 'HEAD'" do 17 | @ref.name.should == 'HEAD' 18 | end 19 | should "target 'ref: refs/heads/master'" do 20 | @ref.targetName.should == 'ref: refs/heads/master' 21 | end 22 | end 23 | describe "ref refs/heads/master" do 24 | before do 25 | @ref = GITRef.refWithName("refs/heads/master", andTarget:simple_repository.commit('Initial commit').sha, inRepo:simple_repository.git_repo) 26 | end 27 | 28 | should "not be nil" do 29 | @ref.should.not.be.nil 30 | end 31 | should "not be a link" do 32 | @ref.should.not.be.link 33 | end 34 | should "have name 'refs/heads/master'" do 35 | @ref.name.should == 'refs/heads/master' 36 | end 37 | should "target sha" do 38 | @ref.targetName.should == simple_repository.commit('Initial commit').sha 39 | end 40 | end 41 | end 42 | 43 | describe "GITRef -resolve" do 44 | describe "with link ref" do 45 | before do 46 | @ref = GITRef.refWithName("HEAD", andTarget:"ref: refs/heads/master", inRepo:simple_repository.git_repo) 47 | @res = @ref.resolve 48 | end 49 | 50 | should "not be nil" do 51 | @res.should.not.be.nil 52 | end 53 | should "return GITRef" do 54 | @res.class.should == GITRef 55 | end 56 | should "return ref with name 'refs/heads/master'" do 57 | @res.name.should == 'refs/heads/master' 58 | end 59 | end 60 | describe "with fixed ref" do 61 | before do 62 | @ref = GITRef.refWithName("refs/heads/master", andTarget:simple_repository.commit('Initial commit').sha, inRepo:simple_repository.git_repo) 63 | @res = @ref.resolve 64 | end 65 | 66 | should "not be nil" do 67 | @res.should.not.be.nil 68 | end 69 | should "be equal to subject" do 70 | @res.should == @ref 71 | end 72 | end 73 | end 74 | 75 | describe 'GITRef -target' do 76 | describe 'tag ref' do 77 | before do 78 | @ref = GITRef.refWithName("v0.0.0", andTarget:simple_repository.tags['v0.0.0'].sha, inRepo:simple_repository.git_repo) 79 | @tgt = @ref.target 80 | end 81 | should 'not be nil' do 82 | @tgt.should.not.be.nil 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /Test/Unit/GITDateTime.rb: -------------------------------------------------------------------------------- 1 | describe 'GITDateTime' do 2 | describe '+dateTimeWithDate:' do 3 | before do 4 | @date = NSDate.date 5 | @zone = NSTimeZone.defaultTimeZone 6 | @dateTime = GITDateTime.dateTimeWithDate(@date) 7 | end 8 | should 'not be nil' do 9 | @dateTime.should.not.be.nil 10 | end 11 | should 'have date' do 12 | @dateTime.date.should === @date 13 | end 14 | should 'have time zone' do 15 | @dateTime.timeZone.should === @zone 16 | end 17 | end 18 | describe '+dateTimeWithDate:timeZone:' do 19 | before do 20 | @date = NSDate.date 21 | @zone = NSTimeZone.timeZoneWithAbbreviation("GMT") 22 | @dateTime = GITDateTime.dateTimeWithDate(@date, timeZone:@zone) 23 | end 24 | should 'not be nil' do 25 | @dateTime.should.not.be.nil 26 | end 27 | should 'have date' do 28 | @dateTime.date.should === @date 29 | end 30 | should 'have time zone' do 31 | @dateTime.timeZone.should === @zone 32 | end 33 | end 34 | describe '+dateTimeWithTimestamp:timeZoneOffset:' do 35 | before do 36 | @date = NSDate.date 37 | @zone = NSTimeZone.timeZoneWithAbbreviation("GMT") 38 | @dateTime = GITDateTime.dateTimeWithTimestamp(@date.timeIntervalSince1970, timeZoneOffset:@zone.offsetString) 39 | end 40 | should 'not be nil' do 41 | @dateTime.should.not.be.nil 42 | end 43 | should 'have date' do 44 | # For some as yet undetermined reason NSDate -isEqualToDate: fails with this 45 | # visual inspection of printed values of these dates seems to suggest they're 46 | # equal but we have to ditch the sub-second precision of the NSTimeInterval. 47 | @dateTime.date.timeIntervalSinceReferenceDate.to_i.should == @date.timeIntervalSinceReferenceDate.to_i 48 | end 49 | should 'have time zone' do 50 | @dateTime.timeZone.should === @zone 51 | end 52 | end 53 | describe '-description' do 54 | before do 55 | @date = NSDate.dateWithTimeIntervalSince1970(444123000) 56 | @zone = NSTimeZone.timeZoneWithAbbreviation("GMT") 57 | @dateTime = GITDateTime.dateTimeWithDate(@date, timeZone:@zone) 58 | end 59 | should 'format the date and timezone' do 60 | @dateTime.description.should == "444123000 +0000" 61 | end 62 | end 63 | describe "-stringWithFormat:" do 64 | before do 65 | @date = NSDate.dateWithTimeIntervalSince1970(444123000) 66 | @zone = NSTimeZone.timeZoneWithAbbreviation("GMT") 67 | @dateTime = GITDateTime.dateTimeWithDate(@date, timeZone:@zone) 68 | @string = @dateTime.stringWithFormat("E MMM dd, yyyy 'at' HH:mm:ss ZZZ") 69 | end 70 | should "format the date and timezone" do 71 | @string.should == "Sat Jan 28, 1984 at 07:30:00 +0000" 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /Vendor/OmniGroup/OmniBase/OBBacktraceBuffer.m: -------------------------------------------------------------------------------- 1 | // Copyright 2008 Omni Development, Inc. All rights reserved. 2 | // 3 | // This software may only be used and reproduced according to the 4 | // terms in the file OmniSourceLicense.html, which should be 5 | // distributed with this project and can also be found at 6 | // . 7 | 8 | #import "OBUtilities.h" 9 | #import "OBBacktraceBuffer.h" 10 | #include // For backtrace() 11 | 12 | #if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__ ) >= 401) 13 | #define BUILTIN_ATOMICS /* GCC 4.1.x has some builtins for atomic operations */ 14 | #else 15 | #import 16 | #endif 17 | 18 | 19 | static struct OBBacktraceBuffer backtraces[OBBacktraceBufferTraceCount]; 20 | static int next_available_backtrace; 21 | static struct OBBacktraceBuffer *OBAcquireBacktraceBuffer(); 22 | 23 | const struct OBBacktraceBufferInfo OBBacktraceBufferInfo = { 24 | OBBacktraceBufferInfoVersionMagic, sizeof(struct OBBacktraceBufferInfo), 25 | OBBacktraceBufferAddressCount, OBBacktraceBufferTraceCount, 26 | (uintptr_t)backtraces, (uintptr_t)&next_available_backtrace 27 | }; 28 | 29 | void OBRecordBacktrace(uintptr_t ctxt, int optype) 30 | { 31 | assert(optype != OBBacktraceBuffer_Unused && optype != OBBacktraceBuffer_Allocated); // 0 and 1 reserved for us 32 | 33 | struct OBBacktraceBuffer *buf = OBAcquireBacktraceBuffer(); 34 | 35 | buf->context = ctxt; 36 | int got = backtrace(buf->frames, OBBacktraceBufferAddressCount); 37 | if (got >= 0) { 38 | while (got < OBBacktraceBufferAddressCount) 39 | buf->frames[got ++] = 0; 40 | } 41 | 42 | // Memory barrier. We want everything we just did to be committed before we update 'type'. 43 | #ifdef BUILTIN_ATOMICS 44 | __sync_synchronize(); 45 | #else 46 | OSMemoryBarrier(); 47 | #endif 48 | 49 | buf->type = optype; 50 | } 51 | 52 | static struct OBBacktraceBuffer *OBAcquireBacktraceBuffer() 53 | { 54 | int slot = next_available_backtrace; 55 | 56 | for(;;) { 57 | int next_slot = ( slot >= ( OBBacktraceBufferTraceCount-1 ) ) ? 0 : slot+1; 58 | int was_slot; 59 | #ifdef BUILTIN_ATOMICS 60 | was_slot = __sync_val_compare_and_swap(&next_available_backtrace, slot, next_slot); 61 | #else 62 | was_slot = OSAtomicCompareAndSwapInt(slot, next_slot, &next_available_backtrace); 63 | #endif 64 | if (__builtin_expect(was_slot == slot, 1)) 65 | break; 66 | else 67 | slot = was_slot; 68 | } 69 | 70 | struct OBBacktraceBuffer *buf = &(backtraces[slot]); 71 | buf->type = OBBacktraceBuffer_Allocated; 72 | 73 | #ifdef BUILTIN_ATOMICS 74 | __sync_synchronize(); 75 | #else 76 | OSMemoryBarrier(); 77 | #endif 78 | 79 | return buf; 80 | } 81 | -------------------------------------------------------------------------------- /Source/GITPackFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFile.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | extern const char const GITPackFileSignature[]; 13 | 14 | @class GITPackIndex, GITPackObject, GITObjectHash; 15 | 16 | /*! 17 | * GITPackFile is a class which provides access to individual 18 | * PACK files within a git repository. 19 | * 20 | * A PACK file is an archive format used by git primarily for 21 | * network transmission of repository objects. Once transmitted 22 | * the received PACK files are then used for access to the stored 23 | * objects. 24 | */ 25 | @interface GITPackFile : NSObject { 26 | 27 | } 28 | 29 | //! \name Creating and Initialising PACK Files 30 | /*! 31 | * Create an autoreleased PACK file with the specified path. 32 | * 33 | * \param packPath Path to the PACK file 34 | * \param error NSError describing the error that occurred 35 | * \return PACK File object or nil if an error occurred 36 | * \sa initWithPath:error: 37 | */ 38 | + (id)packWithPath: (NSString *)packPath error: (NSError **)error; 39 | 40 | /*! 41 | * Initialises a PACK file with the specified path. 42 | * 43 | * \param packPath Path to the PACK file 44 | * \param error NSError describing the error that occurred 45 | * \return PACK File object or nil if an error occurred 46 | * \sa initWithData:indexPath:error: 47 | */ 48 | - (id)initWithPath: (NSString *)packPath error: (NSError **)error; 49 | 50 | /*! 51 | * Initialises a PACK file with the specified data and path to index file. 52 | * 53 | * \param packData NSData object of the PACK file data 54 | * \param indexPath Path to the PACK files Index file 55 | * \param error NSError describing the error that occurred 56 | * \return PACK file object or nil if an error occurred 57 | */ 58 | - (id)initWithData: (NSData *)packData indexPath: (NSString *)indexPath error: (NSError **)error; 59 | 60 | //! \name PACK File Information 61 | /*! 62 | * Returns the PACK file version. 63 | * 64 | * \return Integer version of the PACK file 65 | */ 66 | - (NSUInteger)version; 67 | 68 | /*! 69 | * Returns the index of the receiver 70 | * 71 | * \return index of the receiver 72 | */ 73 | - (GITPackIndex *)index; 74 | 75 | /*! 76 | * Returns the number of objects stored in the receiver 77 | * 78 | * \return number of objects in the receiver 79 | */ 80 | - (NSUInteger)numberOfObjects; 81 | 82 | //! \name Object Extraction 83 | /*! 84 | * Returns a pack object identified by \a objectHash. 85 | * 86 | * The pack object consists of the data required to create a proper git object. 87 | * 88 | * \param objectHash Hash identifying the object data to retrieve 89 | * \param error NSError describing the error which occurred 90 | * \return pack object for the specified \a objectHash or nil if an error occurred 91 | */ 92 | - (GITPackObject *)unpackObjectWithSha1: (GITObjectHash *)objectHash error: (NSError **)error; 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Source/GITPackObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackObject.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITObject.h" 11 | 12 | 13 | /*! 14 | * The GITPackObject class aids in the extraction of objects from PACK files. 15 | * 16 | * The class combines the data of the object and the indicated type for later 17 | * conversion into a normal git object type such as \e commit, \e tree, \e blob 18 | * or \e tag. 19 | * 20 | * When loading an object from a PACK file you do not create instances of this 21 | * class directly, rather you call the -unpackObjectWithSha1:error: method on 22 | * the GITPackFile and an instance of this class is returned. 23 | */ 24 | @interface GITPackObject : NSObject { 25 | GITObjectType type; //!< Type of the object the data represents 26 | NSData *data; //!< Data of the packed object 27 | GITObjectHash *sha1; //!< SHA1 Hash of the packed object 28 | } 29 | 30 | //! \name Properties 31 | @property (assign) GITObjectType type; 32 | @property (copy) NSData *data; 33 | @property (retain) GITObjectHash *sha1; 34 | @property (readonly,assign) NSUInteger length; //!< size of the receivers data 35 | 36 | //! \name Creating and Initialising GITPackObjects 37 | /*! 38 | * Create and return a new PACK object. 39 | * 40 | * \param packData Data to create the PACK object from 41 | * \param objectHash The SHA1 hash of the object 42 | * \param objectType Type of object the \a packData represents 43 | * \return new pack object 44 | * \sa initWithData:type: 45 | */ 46 | + (GITPackObject *)packObjectWithData: (NSData *)packData sha1: (GITObjectHash *)objectHash type: (GITObjectType)objectType; 47 | 48 | /*! 49 | * Create and return a new PACK object. 50 | * 51 | * \param packData Data of the object 52 | * \param objectHash The SHA1 hash of the object 53 | * \param objectType Type of the object the \a packData represents 54 | * \return new pack object 55 | * \sa packObjectWithData:type: 56 | */ 57 | - (id)initWithData: (NSData *)packData sha1: (GITObjectHash *)objectHash type: (GITObjectType)objectType; 58 | 59 | //! \name Obtaining the contained object 60 | /*! 61 | * Returns the object contained in the receiver and sets the \a repo of the object. 62 | * 63 | * \param repo Repository the object should belong to 64 | * \param error NSError describing any errors which occurred 65 | * \return object in the \a repo 66 | * \sa GITPackFile::unpackObjectWithSha1 67 | */ 68 | - (GITObject *)objectInRepo: (GITRepo *)repo error: (NSError **)error; 69 | 70 | //! \name Patching GITPackObjects 71 | /*! 72 | * Returns a new PACK object by patching the receivers data with \a deltaData. 73 | * 74 | * \param deltaData Data to patch the receivers data with 75 | * \return new PACK object with the result of patching the receivers data with \a deltaData 76 | */ 77 | - (GITPackObject *)packObjectByDeltaPatchingWithData: (NSData *)deltaData; 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Source/GITPackIndex.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndex.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 07/11/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndex.h" 10 | #import "GITObjectHash.h" 11 | #import "GITPackIndexPlaceholder.h" 12 | 13 | 14 | const char const GITPackIndexVersionDiscriminator[] = { '\377', 't', 'O', 'c' }; 15 | 16 | GITFanoutEntry GITMakeFanoutEntry(NSUInteger prior, NSUInteger entries) { 17 | GITFanoutEntry e; 18 | e.numberOfPriorEntries = prior; 19 | e.numberOfEntries = entries; 20 | return e; 21 | } 22 | 23 | @implementation GITPackIndex 24 | 25 | + (id)alloc { 26 | if ([self isEqual:[GITPackIndex class]]) 27 | return [GITPackIndexPlaceholder alloc]; 28 | else return [super alloc]; 29 | } 30 | 31 | + (id)allocWithZone: (NSZone *)zone { 32 | if ([self isEqual:[GITPackIndex class]]) 33 | return [GITPackIndexPlaceholder allocWithZone:zone]; 34 | else return [super allocWithZone:zone]; 35 | } 36 | 37 | - (id)copyWithZone: (NSZone *)zone { 38 | return self; 39 | } 40 | 41 | + (id)packIndexWithPath: (NSString *)indexPath error: (NSError **)error { 42 | return [[[[self class] alloc] initWithPath:indexPath error:error] autorelease]; 43 | } 44 | 45 | - (id)initWithPath: (NSString *)indexPath error: (NSError **)error { 46 | [self doesNotRecognizeSelector: _cmd]; 47 | [self release]; 48 | return nil; 49 | } 50 | 51 | - (id)initWithData: (NSData *)indexData error: (NSError **)error { 52 | [self doesNotRecognizeSelector: _cmd]; 53 | [self release]; 54 | return nil; 55 | } 56 | 57 | - (NSUInteger)version { 58 | return 0; 59 | } 60 | 61 | - (NSArray *)fanoutTable { 62 | return [self fanoutTable:NULL]; 63 | } 64 | 65 | - (NSArray *)fanoutTable: (NSError **)error { 66 | [self doesNotRecognizeSelector: _cmd]; 67 | return nil; 68 | } 69 | 70 | - (NSUInteger)numberOfObjects { 71 | return [[[self fanoutTable] lastObject] unsignedIntegerValue]; 72 | } 73 | 74 | - (GITFanoutEntry)fanoutEntryForShasStartingWithByte: (uint8_t)byte { 75 | NSUInteger prev = 0, curr = [[[self fanoutTable] objectAtIndex:byte] unsignedIntegerValue]; 76 | if ( byte != 0x0 ) 77 | prev = [[[self fanoutTable] objectAtIndex:byte - 1] unsignedIntegerValue]; 78 | return GITMakeFanoutEntry(prev, curr - prev); 79 | } 80 | 81 | - (NSUInteger)indexOfSha1: (GITObjectHash *)objectHash { 82 | [self doesNotRecognizeSelector: _cmd]; 83 | return 0; 84 | } 85 | 86 | - (off_t)packOffsetAtIndex: (NSUInteger)idx { 87 | [self doesNotRecognizeSelector: _cmd]; 88 | return 0; 89 | } 90 | 91 | - (off_t)packOffsetForSha1: (GITObjectHash *)objectHash { 92 | return [self packOffsetForSha1:objectHash error:NULL]; 93 | } 94 | 95 | - (off_t)packOffsetForSha1: (GITObjectHash *)objectHash error: (NSError **)error { 96 | [self doesNotRecognizeSelector: _cmd]; 97 | return 0; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Source/GITTreeItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITTreeItem.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 14/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITTreeItem.h" 10 | #import "GITObjectHash.h" 11 | #import "GITTree.h" 12 | #import "GITRepo.h" 13 | 14 | 15 | @implementation GITTreeItem 16 | 17 | @synthesize parent, mode, name, item, sha1; 18 | 19 | + (GITTreeItem *)itemInTree: (GITTree *)tree withMode: (NSUInteger)mode name: (NSString *)name sha1: (GITObjectHash *)sha1 { 20 | return [[[self alloc] initInTree:tree withMode:mode name:name sha1:sha1] autorelease]; 21 | } 22 | 23 | - (id)initInTree: (GITTree *)tree withMode: (NSUInteger)theMode name: (NSString *)theName sha1: (GITObjectHash *)theSha1 { 24 | if ( ![super init] ) 25 | return nil; 26 | 27 | self.parent = tree; 28 | self.mode = theMode; 29 | self.name = theName; 30 | self.sha1 = theSha1; 31 | 32 | return self; 33 | } 34 | 35 | - (GITObject *)item { 36 | if ( !item ) 37 | self.item = [self.parent.repo objectWithSha1:self.sha1 error:NULL]; 38 | return item; 39 | } 40 | 41 | - (void)dealloc { 42 | self.parent = nil; 43 | self.name = nil; 44 | self.sha1 = nil; 45 | if ( item ) 46 | self.item = nil; 47 | 48 | [super dealloc]; 49 | } 50 | 51 | - (BOOL)isLink { 52 | return (mode & GITTreeItemModeLink) == GITTreeItemModeLink; 53 | } 54 | 55 | - (BOOL)isFile { 56 | return (mode & GITTreeItemModeFile) == GITTreeItemModeFile; 57 | } 58 | 59 | - (BOOL)isDirectory { 60 | return (mode & GITTreeItemModeDir) == GITTreeItemModeDir; 61 | } 62 | 63 | - (BOOL)isModule { 64 | return (mode & GITTreeItemModeMod) == GITTreeItemModeMod; 65 | } 66 | 67 | - (NSString *)description { 68 | return [NSString stringWithFormat:@"", self, self.sha1, self.mode, self.name]; 69 | } 70 | 71 | - (NSUInteger)hash { 72 | return [self.sha1 hash]; 73 | } 74 | 75 | - (BOOL)isEqual: (id)other { 76 | if ( !other ) return NO; // other is nil 77 | if ( other == self ) return YES; // pointers match? 78 | 79 | if ( [other isKindOfClass:[self class]] ) // Same class? 80 | return [self isEqualToTreeItem:other]; 81 | if ( [other isKindOfClass:[GITObject class]] ) // A git object? 82 | return [self isEqualToObject:other]; 83 | 84 | return NO; // Definitely not then 85 | } 86 | 87 | - (BOOL)isEqualToTreeItem: (GITTreeItem *)rhs { 88 | if ( !rhs ) return NO; 89 | if ( self == rhs ) return YES; 90 | if ( [self.sha1 isEqualToObjectHash:[rhs sha1]] ) 91 | return YES; 92 | return NO; 93 | } 94 | 95 | - (BOOL)isEqualToObject: (GITObject *)rhs { 96 | if ( !rhs ) return NO; 97 | if ( [self.sha1 isEqualToObjectHash:[rhs sha1]] ) 98 | return YES; 99 | return NO; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /Test/Unit/GITCommit.rb: -------------------------------------------------------------------------------- 1 | require 'zlib' 2 | 3 | class Bacon::Context 4 | def commitDataForSha(sha) 5 | file = "#{simple_repository.root}/.git/objects/%s/%s" % [sha[0,2], sha[2..-1]] 6 | Zlib::Inflate.inflate(File.read(file)).split("\x00", 2)[1].to_data 7 | end 8 | def commitForSha(sha) 9 | d = commitDataForSha(sha) 10 | GITCommit.commitFromData(d, sha1:GITObjectHash.objectHashWithString(sha), repo:@repo, error:@err) 11 | end 12 | end 13 | 14 | describe 'GITCommit' do 15 | before do 16 | @err = Pointer.new(:object) 17 | @repo = simple_repository.git_repo 18 | end 19 | describe "Non-initial commit" do 20 | before do 21 | @commit = commitForSha(simple_repository.commits[1].sha) 22 | @date = simple_repository.commits[1].author_date.to_git 23 | end 24 | should 'not be nil' do 25 | @commit.should.not.be.nil 26 | end 27 | should 'not have an error' do 28 | @err[0].should.be.nil 29 | end 30 | should 'belong to repo' do 31 | @commit.repo.should == @repo 32 | end 33 | should 'reference tree "adc4ee11245f2132df7eaf46851b3dd43870d954"' do 34 | @commit.treeSha1.unpackedString.should == "adc4ee11245f2132df7eaf46851b3dd43870d954" 35 | end 36 | should 'reference parent commit' do 37 | @commit.parentSha1.unpackedString.should == simple_repository.commits[0].sha 38 | end 39 | should 'have cachedData' do 40 | @commit.cachedData.should.not.be.nil 41 | end 42 | should 'have author name' do 43 | @commit.author.name.should == simple_repository.commits[1].author_name 44 | end 45 | should 'have author email' do 46 | @commit.author.email.should == simple_repository.commits[1].author_email 47 | end 48 | should 'have author date' do 49 | @commit.authorDate.date.should === @date.date 50 | @commit.authorDate.timeZone.should === @date.timeZone 51 | end 52 | should 'have committer name' do 53 | @commit.committer.name.should == simple_repository.commits[1].committer_name 54 | end 55 | should 'have committer email' do 56 | @commit.committer.email.should == simple_repository.commits[1].committer_email 57 | end 58 | should 'have committer date ' do 59 | @commit.committerDate.date.should === @date.date 60 | @commit.committerDate.timeZone.should === @date.timeZone 61 | end 62 | should 'have message' do 63 | @commit.message.should == "Update testfile.txt\n" 64 | end 65 | describe '-rawContent' do 66 | before do 67 | @data = commitDataForSha(@commit.sha1.unpackedString) 68 | end 69 | should 'return formatted commit' do 70 | @commit.rawContent.should === @data 71 | end 72 | end 73 | end 74 | describe "initial commit" do 75 | before do 76 | @commit = commitForSha(simple_repository.commits[0].sha) 77 | end 78 | should 'not be nil' do 79 | @commit.should.not.be.nil 80 | end 81 | should 'not have an error' do 82 | puts @err[0].localizedDescription unless @err[0].nil? 83 | @err[0].should.be.nil 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /Source/GITLooseObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITLooseObject.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 14/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITLooseObject.h" 10 | #import "GITObjectHash.h" 11 | #import "GITError.h" 12 | #import "NSData+Searching.h" 13 | #import "NSRangeEnd.h" 14 | #import "NSData+Compression.h" 15 | 16 | 17 | @implementation GITLooseObject 18 | 19 | @synthesize type, data, sha1; 20 | 21 | + (GITLooseObject *)looseObjectWithSha1: (GITObjectHash *)objectHash from: (NSString *)directory error: (NSError **)error { 22 | return [[[self alloc] initWithSha1:objectHash from:directory error:error] autorelease]; 23 | } 24 | 25 | - (id)initWithSha1: (GITObjectHash *)objectHash from: (NSString *)directory error: (NSError **)error { 26 | if ( ![super init] ) 27 | return nil; 28 | 29 | BOOL isDirectory; 30 | NSFileManager *fm = [NSFileManager defaultManager]; 31 | if ( ![fm fileExistsAtPath:directory isDirectory:&isDirectory] || !isDirectory ) { 32 | GITError(error, GITLooseObjectErrorDirectoryDoesNotExist, NSLocalizedString(@"Directory path is not a directory or does not exist", @"GITLooseObjectErrorDirectoryDoesNotExist")); 33 | [self release]; 34 | return nil; 35 | } 36 | 37 | self.sha1 = objectHash; 38 | 39 | NSString *sha1String = [self.sha1 unpackedString]; 40 | NSString *pathSuffix = [NSString stringWithFormat:@"%@/%@", [sha1String substringToIndex:2], [sha1String substringFromIndex:2]]; 41 | NSString *objectPath = [directory stringByAppendingPathComponent:pathSuffix]; 42 | 43 | if ( ![fm fileExistsAtPath:objectPath isDirectory:&isDirectory] || isDirectory ) { 44 | GITError(error, GITLooseObjectErrorObjectNotFound, NSLocalizedString(@"Object could not be found", @"GITLooseObjectErrorObjectNotFound")); 45 | [self release]; 46 | return nil; 47 | } 48 | 49 | NSData *zlibData = [[NSData alloc] initWithContentsOfFile:objectPath options:NSDataReadingMapped error:error]; 50 | if ( !zlibData ) // If there was a problem reading the data... 51 | return nil; 52 | 53 | NSData *fileData = [zlibData zlibInflate]; [zlibData release]; 54 | NSRange typeRange = [fileData rangeFrom:0 toByte:' ']; 55 | NSString *typeStr = [[NSString alloc] initWithBytes:[fileData bytes] length:NSRangeEnd(typeRange) encoding:NSASCIIStringEncoding]; 56 | 57 | self.type = [GITObject objectTypeForString:typeStr]; 58 | [typeStr release]; 59 | 60 | NSRange prefixRange = [fileData rangeFrom:NSRangeEnd(typeRange) toByte:0x0]; // We don't care about the size 61 | self.data = [fileData subdataFromIndex:NSRangeEnd(prefixRange) + 1]; 62 | 63 | return self; 64 | } 65 | 66 | - (void)dealloc { 67 | self.data = nil; 68 | self.sha1 = nil; 69 | [super dealloc]; 70 | } 71 | 72 | - (NSUInteger)length { 73 | return [self.data length]; 74 | } 75 | 76 | - (GITObject *)objectInRepo: (GITRepo *)repo error: (NSError **)error { 77 | return [GITObject objectOfType:self.type withData:self.data sha1:self.sha1 repo:repo error:error]; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Test/Unit/GITPackIndexWriter.rb: -------------------------------------------------------------------------------- 1 | describe "GITPackIndexWriter" do 2 | before do 3 | @err = Pointer.new(:object) 4 | @sha1 = GITObjectHash.objectHashWithString('bd94b5ea8ab503e4e7676ab4668f5f1ec1f523ea') 5 | @repo = simple_repository.git_repo 6 | @data = @repo.objectWithSha1(@sha1, error:@err).rawContent.zlibDeflate 7 | 8 | @writer = GITPackIndexWriter.indexWriterVersion(2) 9 | end 10 | should 'not be nil' do 11 | @writer.should.not.be.nil 12 | end 13 | describe "-addObjectWithName:andData:atOffset:" do 14 | before do 15 | @writer.addObjectWithName(@sha1, andData:@data, atOffset:12) 16 | end 17 | should "increase the number of known objects" do 18 | @writer.objects.count.should.be > 0 19 | end 20 | should "increase the fanout table counter" do 21 | @writer.fanoutTable[@sha1.firstPackedByte].should.be > 0 22 | end 23 | end 24 | describe "-writeToStream:error:" do 25 | before do 26 | @streamErr = Pointer.new(:object) 27 | @stream = NSOutputStream.outputStreamToMemory 28 | end 29 | describe "version one" do 30 | before do 31 | @writer = GITPackIndexWriter.indexWriterVersion(1) 32 | @writer.addObjectWithName(@sha1, andData:@data, atOffset:12) 33 | @result = @writer.writeToStream(@stream, error:@streamErr) 34 | @output = @stream.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) 35 | @index = GITPackIndex.alloc.initWithData(@output, error:@err) 36 | end 37 | should "not have an error" do 38 | @streamErr[0].should.be.nil 39 | end 40 | should "have written to the stream" do 41 | @output.should.not.be.nil 42 | end 43 | should "return 0" do 44 | @result.should == 0 45 | end 46 | should "be a version one index" do 47 | @index.version.should == 1 48 | end 49 | should "have one object" do 50 | @index.numberOfObjects.should == 1 51 | end 52 | should "have pack offset for indexed object" do 53 | @index.packOffsetForSha1(@sha1).should == 12 54 | end 55 | end 56 | describe "version two" do 57 | before do 58 | @writer = GITPackIndexWriter.indexWriterVersion(2) 59 | @writer.addObjectWithName(@sha1, andData:@data, atOffset:12) 60 | @result = @writer.writeToStream(@stream, error:@streamErr) 61 | @output = @stream.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) 62 | @index = GITPackIndex.alloc.initWithData(@output, error:@err) 63 | end 64 | should "not have an error" do 65 | @streamErr[0].should.be.nil 66 | end 67 | should "have written to the stream" do 68 | @output.should.not.be.nil 69 | end 70 | should "return 0" do 71 | @result.should == 0 72 | end 73 | should "be a version one index" do 74 | @index.version.should == 2 75 | end 76 | should "have one object" do 77 | @index.numberOfObjects.should == 1 78 | end 79 | should "have pack offset for indexed object" do 80 | @index.packOffsetForSha1(@sha1).should == 12 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /Source/GITTag.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITTag.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 13/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITObject.h" 11 | 12 | 13 | @class GITObjectHash, GITActor, GITDateTime; 14 | 15 | /*! 16 | * This class represents \e Tag objects in a git repository. 17 | * 18 | * Tag objects attach a conventional name to another object in the repository, 19 | * typically a commit. The creation of the tag is dated and attributed to a 20 | * specific actor within the repository, freqently a description of the meaning 21 | * of the name is included. 22 | * 23 | * \todo Document the internal structure of the tag object 24 | * \verbatim 25 | * object 1615307cc4523f183e777df67f168c86908e8007 26 | * type commit 27 | * tag v1.0.0 28 | * tagger A. Developer 444123000 +0000 29 | * 30 | * A tag message meant to describe what this tag defines 31 | * \endverbatim 32 | */ 33 | @interface GITTag : GITObject { 34 | NSString *name; //!< Name of the tag 35 | 36 | GITObjectType targetType; //!< Type of the target so we can get it later 37 | GITObjectHash *targetSha1; //!< Hash of the target so we can get it later 38 | GITObject *target; //!< Cache of the target once we've found it 39 | 40 | GITActor *tagger; //!< Actor who created the tag 41 | GITDateTime *taggerDate; //!< Date the tag was created 42 | 43 | NSString *message; //!< Tag message 44 | 45 | NSData *cachedData; 46 | } 47 | 48 | //! \name Properties 49 | @property (copy) NSString *name; 50 | @property (retain) GITObject *target; 51 | @property (retain) GITActor *tagger; 52 | @property (retain) GITDateTime *taggerDate; 53 | @property (copy) NSString *message; 54 | 55 | //! \name Creating and Initialising Tags 56 | /*! 57 | * Creates and returns a tag from the \a data. 58 | * 59 | * The \a data content is parsed to extract the information about the tag such 60 | * as the name, referenced object, tagger, dates and the tag message. 61 | * 62 | * \param data The data describing the tag 63 | * \param objectHash The SHA1 hash of the receiver 64 | * \param repo The repository the tag is a member of 65 | * \param error NSError describing the error that occurred 66 | * \return A tag object from the \a data 67 | */ 68 | + (GITTag *)tagFromData: (NSData *)data sha1: (GITObjectHash *)objectHash repo: (GITRepo *)repo error: (NSError **)error; 69 | 70 | //! \name Testing Object Target 71 | /*! 72 | * Checks if the receivers target object hash matches the \a objectHash given. 73 | * 74 | * \param objectHash Object Hash to compare with the receivers target object hash 75 | * \return YES if the object hash matches that of the targets object hash, NO otherwise. 76 | */ 77 | - (BOOL)refersToObjectHash: (GITObjectHash *)objectHash; 78 | 79 | /*! 80 | * Checks if the receivers target matches the \a object given. 81 | * 82 | * \param object GITObject to compare with the receivers target object. 83 | * \return YES if the \a object matches that of the receivers target object, NO otherwise. 84 | */ 85 | - (BOOL)refersToObject: (GITObject *)object; 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /Source/GITLooseObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITLooseObject.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 14/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITObject.h" 11 | 12 | 13 | /*! 14 | * The GITLooseObject class aids in the extraction of objects from loose files. 15 | * 16 | * The class combines the data of the object and the indicated type for later 17 | * conversion into a normal git object type such as \e commit, \e tree, \e blob 18 | * or \e tag. 19 | * 20 | * When loading an object from the file system you create an instance of this 21 | * class directly, this is opposed to the way in which is PACK counterpart 22 | * GITPackObject is used. 23 | */ 24 | @interface GITLooseObject : NSObject { 25 | GITObjectType type; //!< Type of the object the data represents 26 | NSData *data; //!< Data of the object 27 | GITObjectHash *sha1; //!< SHA1 hash of the object 28 | } 29 | 30 | //! \name Properties 31 | @property (assign) GITObjectType type; 32 | @property (copy) NSData *data; 33 | @property (retain) GITObjectHash *sha1; 34 | @property (assign,readonly) NSUInteger length; //!< Size of the receivers data 35 | 36 | //! \name Creating and Initialising Loose Objects 37 | /*! 38 | * Creates and returns a loose object identified by the \a objectHash from the \a directory specified. 39 | * 40 | * Loads data from the file system for the object matching the \a objectHash. The object prefix is extracted 41 | * from the loaded data to determine the object type of the data. 42 | * 43 | * \param objectHash Hash identifying the object to retrieve 44 | * \param directory Directory to locate the object within 45 | * \param error NSError describing any errors which occurred 46 | * \return Loose object containing the type and data of the object or nil if not found or an error occurred 47 | * \sa initWithSha1:from:error: 48 | */ 49 | + (GITLooseObject *)looseObjectWithSha1: (GITObjectHash *)objectHash from: (NSString *)directory error: (NSError **)error; 50 | 51 | /*! 52 | * Creates and returns a loose object identified by the \a objectHash from the \a directory specified. 53 | * 54 | * Loads data from the file system for the object matching the \a objectHash. The object prefix is extracted 55 | * from the loaded data to determine the object type of the data. 56 | * 57 | * \param objectHash Hash identifying the object to retrieve 58 | * \param directory Directory to locate the object within 59 | * \param error NSError describing any errors which occurred 60 | * \return Loose object containing the type and data of the object or nil if not found or an error occurred 61 | * \sa looseObjectWithSha1:from:error: 62 | */ 63 | - (id)initWithSha1: (GITObjectHash *)objectHash from: (NSString *)directory error: (NSError **)error; 64 | 65 | //! \name Obtaining the contained object 66 | /*! 67 | * Returns the object contained in the receiver and sets the \a repo of the object. 68 | * 69 | * \param repo Repository the object should belong to 70 | * \param error NSError describing any errors which occurred 71 | * \return object in the \a repo 72 | */ 73 | - (GITObject *)objectInRepo: (GITRepo *)repo error: (NSError **)error; 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Source/GITPackedRefsEnumerator.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackedRefsEnumerator.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 04/10/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackedRefsEnumerator.h" 10 | #import "GITRepo.h" 11 | #import "NSData+Searching.h" 12 | 13 | 14 | @interface GITPackedRefsEnumerator () 15 | @property (copy) NSString *packedRefsPath; 16 | @property (retain) NSData *packedRefs; 17 | @end 18 | 19 | @implementation GITPackedRefsEnumerator 20 | 21 | @synthesize packedRefsPath, packedRefs; 22 | 23 | + (GITPackedRefsEnumerator *)enumeratorForRepo: (GITRepo *)theRepo { 24 | return [[[[self class] alloc] initWithRepo:theRepo] autorelease]; 25 | } 26 | 27 | - (id)initWithRepo: (GITRepo *)theRepo { 28 | if ( ![super init] ) 29 | return nil; 30 | 31 | started = NO; 32 | hasPackedRefs = NO; // assume the worst 33 | self.packedRefsPath = [theRepo.root stringByAppendingPathComponent:@"packed-refs"]; 34 | 35 | BOOL isDirectory; 36 | if ( [[NSFileManager defaultManager] fileExistsAtPath:self.packedRefsPath isDirectory:&isDirectory] && !isDirectory ) { 37 | hasPackedRefs = YES; 38 | } 39 | 40 | return self; 41 | } 42 | 43 | - (void)dealloc { 44 | self.packedRefsPath = nil; 45 | self.packedRefs = nil; 46 | [super dealloc]; 47 | } 48 | 49 | #pragma mark - 50 | #pragma mark NSEnumerator Methods 51 | - (NSArray *)allObjects { 52 | if ( !hasPackedRefs ) 53 | return [NSArray array]; 54 | 55 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:1]; 56 | 57 | id packedRef; 58 | while ( packedRef = [self nextObject] ) { 59 | [array addObject:packedRef]; 60 | } 61 | 62 | return [NSArray arrayWithArray:array]; 63 | } 64 | 65 | - (id)nextObject { 66 | if ( !hasPackedRefs ) 67 | return nil; 68 | 69 | if ( !started ) { 70 | self.packedRefs = [NSData dataWithContentsOfFile:self.packedRefsPath options:NSDataReadingMapped error:NULL]; 71 | currentRange = NSMakeRange(0, 0); 72 | started = YES; 73 | } 74 | 75 | if ( !self.packedRefs ) 76 | return nil; 77 | 78 | currentRange = [self.packedRefs rangeFrom:(currentRange.location + currentRange.length) toByte:'\n']; 79 | if ( currentRange.location == NSNotFound || currentRange.location + currentRange.length > [self.packedRefs length] ) 80 | return nil; 81 | 82 | NSData *packedRefData = [self.packedRefs subdataWithRange:currentRange]; 83 | NSString *packedRefLine = [[NSString alloc] initWithData:packedRefData encoding:NSUTF8StringEncoding]; 84 | 85 | currentRange.length += 1; // move length past the \n 86 | 87 | if ( [packedRefLine hasPrefix:@"#"] ) { 88 | [packedRefLine release]; 89 | return [self nextObject]; 90 | } 91 | 92 | return [packedRefLine autorelease]; 93 | } 94 | 95 | // Included in case we need to add our own NSFastEnumeration implementation 96 | //#pragma mark - 97 | //#pragma mark NSFastEnumerator Methods 98 | //- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len { 99 | // 100 | //} 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /Source/GITPackCollection.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackCollection.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 16/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITPackFile, GITPackObject, GITObjectHash; 13 | 14 | /*! 15 | * This class implements a proxy for unpacking objects from a collection of 16 | * GITPackFile instances. 17 | * 18 | * A git repository might contain a number of PACK files, each of these are 19 | * independent of each other and contain different sets of data. To aid extraction 20 | * of objects from the collection of PACK files this class provides a means 21 | * to iterate over the collection to find objects. 22 | * 23 | * The collection also keeps track of which GITPackFile it last successfully 24 | * extracted an object from, this is because linked objects are likely to be 25 | * stored within the same PACK file. 26 | */ 27 | @interface GITPackCollection : NSObject { 28 | NSArray *collection; 29 | GITPackFile *recent; 30 | } 31 | 32 | //! \name Creating and Initialising Collections 33 | /*! 34 | * Creates and returns a new collection with the \c pack files in the \a directory. 35 | * 36 | * \param directory The path to the directory to collect the pack files from 37 | * \param error NSError describing any errors which occurred 38 | * \return a collection with the pack files from the \a directory 39 | * \sa collectionWithPackFiles: 40 | * \sa initWithPackFilesInDirectory:error: 41 | * \sa initWithPackFiles: 42 | */ 43 | + (GITPackCollection *)collectionWithContentsOfDirectory: (NSString *)directory error: (NSError **)error; 44 | 45 | /*! 46 | * Creates and returns a new collection from the array of \a files. 47 | * 48 | * \param files Array of GITPackFile objects 49 | * \return new collection 50 | * \sa initWithPackFiles: 51 | */ 52 | + (GITPackCollection *)collectionWithPackFiles: (NSArray *)files; 53 | 54 | /*! 55 | * Creates and returns a new collection with the \c pack files in the \a directory. 56 | * 57 | * \param directory The path to the directory to collect the pack files from 58 | * \param error NSError describing any errors which occurred 59 | * \return a collection with the pack files from the \a directory 60 | * \sa initWithPackFiles: 61 | */ 62 | - (id)initWithPackFilesInDirectory: (NSString *)directory error: (NSError **)error; 63 | 64 | /*! 65 | * Creates and returns a new collection from the array of \a files. 66 | * 67 | * \param files Array of GITPackFile objects 68 | * \return new collection 69 | * \sa initWithPackFiles: 70 | */ 71 | - (id)initWithPackFiles: (NSArray *)files; 72 | 73 | //! \name Object Extraction 74 | /*! 75 | * Returns the first pack object found in the collection with the \a objectHash. 76 | * 77 | * If this method returns nil then it means that the object could not be found 78 | * in the collection, if there is an error then the \a error will not be nil. 79 | * 80 | * \param objectHash Hash identifying the object data to retrieve 81 | * \param error NSError describing the error which occurred 82 | * \return pack object for the specified \a objectHash or nil if not found 83 | */ 84 | - (GITPackObject *)unpackObjectWithSha1: (GITObjectHash *)objectHash error: (NSError **)error; 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriter.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriter.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexWriter.h" 10 | #import "GITPackIndexWriterPlaceholder.h" 11 | 12 | 13 | @implementation GITPackIndexWriter 14 | 15 | #pragma mark Class Cluster Methods 16 | + (id)alloc { 17 | if ([self isEqual:[GITPackIndexWriter class]]) 18 | return [GITPackIndexWriterPlaceholder alloc]; 19 | else return [super alloc]; 20 | } 21 | + (id)allocWithZone: (NSZone *)zone { 22 | if ([self isEqual:[GITPackIndexWriter class]]) 23 | return [GITPackIndexWriterPlaceholder allocWithZone:zone]; 24 | else return [super allocWithZone:zone]; 25 | } 26 | - (id)copyWithZone: (NSZone *)zone { 27 | return self; 28 | } 29 | 30 | #pragma mark Class Initialisers 31 | + (GITPackIndexWriter *)indexWriter { 32 | return [[[self alloc] initWithDefaultVersion] autorelease]; 33 | } 34 | + (GITPackIndexWriter *)indexWriterVersion: (NSUInteger)version { 35 | return [[[self alloc] initWithVersion:version error:NULL] autorelease]; 36 | } 37 | 38 | #pragma mark Initialisers 39 | - (id)initWithDefaultVersion { 40 | [self doesNotRecognizeSelector: _cmd]; 41 | [self release]; 42 | return nil; 43 | } 44 | - (id)initWithVersion: (NSUInteger)version error: (NSError **)error { 45 | [self doesNotRecognizeSelector: _cmd]; 46 | [self release]; 47 | return nil; 48 | } 49 | 50 | #pragma mark Un-implemented Instance Methods 51 | - (void)addObjectWithName: (GITObjectHash *)sha1 andData: (NSData *)data atOffset: (NSUInteger)offset { 52 | [self doesNotRecognizeSelector: _cmd]; 53 | return; 54 | } 55 | - (void)addPackChecksum: (NSData *)packChecksumData { 56 | [self doesNotRecognizeSelector: _cmd]; 57 | return; 58 | } 59 | - (NSInteger)writeToStream: (NSOutputStream *)stream { 60 | [self doesNotRecognizeSelector: _cmd]; 61 | return -1; 62 | } 63 | - (void)prepareForWriting { 64 | [self doesNotRecognizeSelector: _cmd]; 65 | return; 66 | } 67 | 68 | #pragma mark NSRunLoop method 69 | - (void)writeToStream: (NSOutputStream *)stream inRunLoop: (NSRunLoop *)runLoop { 70 | [self prepareForWriting]; 71 | [stream setDelegate:self]; 72 | [stream scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode]; 73 | [stream open]; 74 | } 75 | - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { 76 | switch ( eventCode ) { 77 | case NSStreamEventHasSpaceAvailable: 78 | [self writeToStream:(NSOutputStream *)stream]; 79 | break; 80 | case NSStreamEventErrorOccurred: 81 | // should really handle this 82 | break; 83 | } 84 | } 85 | 86 | #pragma mark Polling Method 87 | - (NSInteger)writeToStream: (NSOutputStream *)stream error: (NSError **)error { 88 | [self prepareForWriting]; 89 | [stream open]; 90 | 91 | while ( [stream hasSpaceAvailable] ) { 92 | if ( [self writeToStream:stream] < 0 ) { 93 | if ( error ) *error = [stream streamError]; 94 | return -1; 95 | } 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriter.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriter.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITObjectHash; 13 | 14 | /*! 15 | * GITPackIndexWriter is a class cluster which provides the ability to 16 | * create both version one and version two PACK IDX files from data 17 | * provided by GITPackFileWriter. 18 | */ 19 | @interface GITPackIndexWriter : NSObject { } 20 | 21 | //! \name Creating and Initialising GITPackIndexWriter objects 22 | /*! 23 | * Creates an autoreleased PACK index writer for the default index version. 24 | * 25 | * \return PACK index writer for the default version 26 | */ 27 | + (GITPackIndexWriter *)indexWriter; 28 | 29 | /*! 30 | * Creates an autoreleased PACK index writer for the specified \a version. 31 | * 32 | * \param version Version of writer to create 33 | * \return PACK index writer with the specified \a version, nil if \a version is unsupported. 34 | */ 35 | + (GITPackIndexWriter *)indexWriterVersion: (NSUInteger)version; 36 | 37 | /*! 38 | * Initialises a PACK index writer for the default version. 39 | * 40 | * \return PACK index writer for the default version 41 | */ 42 | - (id)initWithDefaultVersion; 43 | 44 | /*! 45 | * Initialises a PACK index writer for the specified \a version. 46 | * 47 | * \param version Version of the writer to create 48 | * \param[out] error NSError describing the error that occurred 49 | * \return PACK index writer for the specified \a version, or nil on error 50 | */ 51 | - (id)initWithVersion: (NSUInteger)version error: (NSError **)error; 52 | 53 | //! \name Adding to the index 54 | /*! 55 | * Adds an object to the receiver. 56 | * 57 | * \param sha1 Object hash name of the object being added to the receiver 58 | * \param data Data contents of the object being added to the receiver 59 | * \param offset Offset into the corresponding PACK file where the object is located 60 | */ 61 | - (void)addObjectWithName: (GITObjectHash *)sha1 andData: (NSData *)data atOffset: (NSUInteger)offset; 62 | 63 | /*! 64 | * Adds the PACK checksum data to the receiver. 65 | * 66 | * \param packChecksumData NSData object of the corresponding PACK checksum 67 | */ 68 | - (void)addPackChecksum: (NSData *)packChecksumData; 69 | 70 | //! \name Writing the PACK Index 71 | /*! 72 | * Writes the PACK index contents to the provided \a stream in the NSRunLoop specified. 73 | * 74 | * \todo We need a way of getting any stream errors sent back to the client 75 | * \param stream Output stream to write the PACK index data to 76 | * \param runLoop NSRunLoop to schedule the writing in 77 | */ 78 | - (void)writeToStream: (NSOutputStream *)stream inRunLoop: (NSRunLoop *)runLoop; 79 | 80 | /*! 81 | * Writes the PACK index contents to the provided \a steam. 82 | * 83 | * This method using Polling rather than run-loop scheduling to perform the writing. 84 | * 85 | * \param stream Output stream to write the PACK index data to 86 | * \param[out] error NSError describing any error which occurred 87 | * \return 0 if writing was successful, -1 if an error occurred. 88 | */ 89 | - (NSInteger)writeToStream: (NSOutputStream *)stream error: (NSError **)error; 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /Source/GITPackCollection.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackCollection.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 16/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackCollection.h" 10 | #import "GITPackFile.h" 11 | #import "GITError.h" 12 | 13 | 14 | @interface GITPackCollection () 15 | @property (copy) NSArray *collection; 16 | @property (assign) GITPackFile *recent; 17 | @end 18 | 19 | @implementation GITPackCollection 20 | 21 | @synthesize collection, recent; 22 | 23 | + (GITPackCollection *)collectionWithContentsOfDirectory: (NSString *)directory error: (NSError **)error { 24 | return [[[self alloc] initWithPackFilesInDirectory:directory error:error] autorelease]; 25 | } 26 | 27 | + (GITPackCollection *)collectionWithPackFiles: (NSArray *)files { 28 | return [[[self alloc] initWithPackFiles:files] autorelease]; 29 | } 30 | 31 | - (id)initWithPackFilesInDirectory: (NSString *)directory error: (NSError **)error { 32 | BOOL isDirectory; 33 | NSFileManager *fm = [NSFileManager defaultManager]; 34 | 35 | // Make sure we've got a directory that exists, oh and that its a directory 36 | if ( ![fm fileExistsAtPath:directory isDirectory:&isDirectory] || !isDirectory ) { 37 | GITError(error, GITPackCollectionErrorDirectoryDoesNotExist, NSLocalizedString(@"Directory path is not a directory or does not exist", @"GITPackCollectionErrorDirectoryDoesNotExist")); 38 | [self release]; 39 | return nil; 40 | } 41 | 42 | // Lets get the files 43 | GITPackFile *packFile; 44 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1]; 45 | for ( NSString *file in [fm enumeratorAtPath:directory] ) { 46 | if ( ![[file pathExtension] isEqualToString:@"pack"] ) 47 | continue; 48 | 49 | packFile = [GITPackFile packWithPath:[directory stringByAppendingPathComponent:file] error:error]; 50 | if ( !packFile ) { 51 | [array release]; 52 | [self release]; 53 | return nil; 54 | } 55 | 56 | [array addObject:packFile]; 57 | } 58 | 59 | id object = [self initWithPackFiles:array]; 60 | [array release]; 61 | return object; 62 | } 63 | 64 | - (id)initWithPackFiles: (NSArray *)files { 65 | if ( ![super init] ) 66 | return nil; 67 | 68 | self.recent = nil; 69 | self.collection = files; 70 | return self; 71 | } 72 | 73 | - (void)dealloc { 74 | self.collection = nil; 75 | [super dealloc]; 76 | } 77 | 78 | - (GITPackObject *)unpackObjectWithSha1: (GITObjectHash *)objectHash error: (NSError **)error { 79 | GITPackObject *packObject = nil; 80 | 81 | if ( recent ) { 82 | packObject = [self.recent unpackObjectWithSha1:objectHash error:error]; 83 | if ( packObject ) 84 | return packObject; 85 | } 86 | 87 | for ( GITPackFile *packFile in self.collection ) { 88 | if ( packFile == recent ) 89 | continue; 90 | 91 | packObject = [packFile unpackObjectWithSha1:objectHash error:error]; 92 | if ( packObject ) { 93 | self.recent = packFile; 94 | return packObject; 95 | } 96 | } 97 | 98 | return nil; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /Test/Unit/GITPackFileWriter.rb: -------------------------------------------------------------------------------- 1 | describe "GITPackFileWriter" do 2 | before do 3 | @err = Pointer.new(:object) 4 | @sha1 = GITObjectHash.objectHashWithString(simple_repository.head.sha) 5 | @repo = simple_repository.git_repo 6 | @commit = @repo.objectWithSha1(@sha1, error:@err) 7 | 8 | @writer = GITPackFileWriter.packWriterVersion(2) 9 | end 10 | should 'not be nil' do 11 | @writer.should.not.be.nil 12 | end 13 | describe "-addObjectsFromCommit:" do 14 | before do 15 | @writer.addObjectsFromCommit(@commit) 16 | end 17 | should "have added objects" do 18 | @writer.objects.count.should.be > 0 19 | end 20 | end 21 | describe "-fileName" do 22 | before do 23 | simple_repository.repack 24 | @pack = simple_repository.pack_files.first 25 | @writer.addObjectsFromCommit(@commit) 26 | end 27 | should "be derived from the contained objects" do 28 | @writer.fileName.should == File.basename(@pack) 29 | end 30 | end 31 | describe "-writeToStream:error:" do 32 | before do 33 | @streamErr = Pointer.new(:object) 34 | @stream = NSOutputStream.outputStreamToMemory 35 | @writer.addObjectsFromCommit(@commit) 36 | end 37 | describe "without an indexWriter" do 38 | before do 39 | @result = @writer.writeToStream(@stream, error:@streamErr) 40 | @output = @stream.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) 41 | end 42 | should "not have an error" do 43 | @streamErr[0].should.be.nil 44 | end 45 | should "have written to the stream" do 46 | @output.should.not.be.nil 47 | end 48 | should "return 0" do 49 | @result.should == 0 50 | end 51 | should 'write the signature' do 52 | @output.subdataWithRange(NSMakeRange(0,4)).to_str.should == 'PACK' 53 | end 54 | should 'write the version number' do 55 | @output.subdataWithRange(NSMakeRange(4,4)).to_str.unpack('N')[0].should == 2 56 | end 57 | end 58 | describe "with an indexWriter" do 59 | before do 60 | @idxErr = Pointer.new(:object) 61 | @idxStrm = NSOutputStream.outputStreamToMemory 62 | @idxWriter = GITPackIndexWriter.indexWriter 63 | 64 | # TODO: Fix this API, its a bit rubbish really 65 | @writer.indexWriter = @idxWriter 66 | @result = @writer.writeToStream(@stream, error:@streamErr) 67 | @idxRes = @idxWriter.writeToStream(@idxStrm, error:@idxErr) 68 | 69 | @output = @stream.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) 70 | @idxOut = @idxStrm.propertyForKey(NSStreamDataWrittenToMemoryStreamKey) 71 | 72 | @indxFile = GITPackIndex.alloc.initWithData(@idxOut, error:@err) 73 | @packFile = GITPackFile.alloc.initWithData(@output, indexPath:nil, error:@err) 74 | @packFile.index = @indxFile 75 | end 76 | should "not have stream errors" do 77 | @streamErr[0].should.be.nil 78 | @idxErr[0].should.be.nil 79 | end 80 | should "have written to the stream" do 81 | @output.should.not.be.nil 82 | @idxOut.should.not.be.nil 83 | end 84 | should "return 0" do 85 | @result.should == 0 86 | @idxRes.should == 0 87 | end 88 | should "have matching numbers of objects" do 89 | @packFile.numberOfObjects.should == @indxFile.numberOfObjects 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+SHA1.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+SHA1.m 3 | // 4 | // Created by Geoffrey Garside on 29/06/2008. 5 | // Copyright (c) 2008 Geoff Garside 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | // Based on, believed to be public domain, source code available at 26 | // http://www.cocoadev.com/index.pl?NSDataCategory, though modified 27 | // to use the CommonCrypto API available on Mac OS and iPhone platforms. 28 | // 29 | 30 | #import "NSData+SHA1.h" 31 | #include 32 | 33 | @implementation NSData (SHA1) 34 | 35 | #pragma mark - 36 | #pragma mark SHA1 Hashing macros 37 | #define HEComputeDigest(method) \ 38 | CC_##method##_CTX ctx; \ 39 | unsigned char digest[CC_##method##_DIGEST_LENGTH]; \ 40 | CC_##method##_Init(&ctx); \ 41 | CC_##method##_Update(&ctx, [self bytes], [self length]); \ 42 | CC_##method##_Final(digest, &ctx); 43 | 44 | #define HEComputeDigestNSData(method) \ 45 | HEComputeDigest(method) \ 46 | return [NSData dataWithBytes:digest length:CC_##method##_DIGEST_LENGTH]; 47 | 48 | #define HEComputeDigestNSString(method) \ 49 | static char __HEHexDigits[] = "0123456789abcdef"; \ 50 | unsigned char digestString[2*CC_##method##_DIGEST_LENGTH + 1]; \ 51 | unsigned int i; \ 52 | HEComputeDigest(method) \ 53 | for(i=0; i> 4]; \ 55 | digestString[2*i+1] = __HEHexDigits[digest[i] & 0x0f]; \ 56 | } \ 57 | digestString[2*CC_##method##_DIGEST_LENGTH] = '\0'; \ 58 | return [NSString stringWithCString:(char *)digestString encoding:NSASCIIStringEncoding]; 59 | 60 | #pragma mark - 61 | #pragma mark SHA1 Hashing routines 62 | - (NSData*) sha1Digest 63 | { 64 | HEComputeDigestNSData(SHA1); 65 | } 66 | - (NSString*) sha1DigestString 67 | { 68 | HEComputeDigestNSString(SHA1); 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Source/GITObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITObject.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 08/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITObject.h" 10 | #import "GITObjectHash.h" 11 | #import "GITRepo.h" 12 | #import "GITCommit.h" 13 | #import "GITTree.h" 14 | #import "GITBlob.h" 15 | #import "GITTag.h" 16 | 17 | 18 | @implementation GITObject 19 | 20 | @synthesize type, sha1, repo, size; 21 | 22 | + (Class)objectClassForType: (GITObjectType)type { 23 | switch ( type ) { 24 | case GITObjectTypeCommit: 25 | return [GITCommit class]; 26 | case GITObjectTypeTree: 27 | return [GITTree class]; 28 | case GITObjectTypeBlob: 29 | return [GITBlob class]; 30 | case GITObjectTypeTag: 31 | return [GITTag class]; 32 | default: 33 | return nil; 34 | } 35 | } 36 | 37 | + (NSString *)stringForObjectType: (GITObjectType)type { 38 | return [[self objectClassForType:type] typeName]; 39 | } 40 | 41 | + (GITObjectType)objectTypeForString: (NSString *)type { 42 | unsigned char fchar = [type characterAtIndex:0]; 43 | switch ( fchar ) { 44 | case 'c': 45 | return GITObjectTypeCommit; 46 | case 'b': 47 | return GITObjectTypeBlob; 48 | case 't': 49 | if ( [type characterAtIndex:1] == 'r' ) 50 | return GITObjectTypeTree; 51 | return GITObjectTypeTag; 52 | default: 53 | return GITObjectTypeUnknown; 54 | } 55 | } 56 | 57 | + (id)objectOfType: (GITObjectType)type withData: (NSData *)data sha1: (GITObjectHash *)objectHash repo: (GITRepo *)repo error: (NSError **)error { 58 | Class objectClass = [self objectClassForType:type]; 59 | return [[[objectClass alloc] initFromData:data sha1:objectHash repo:repo error:error] autorelease]; 60 | } 61 | 62 | - (id)initWithType: (GITObjectType)theType sha1: (GITObjectHash *)theSha1 repo: (GITRepo *)theRepo { 63 | if ( ![super init] ) 64 | return nil; 65 | 66 | self.type = theType; 67 | self.sha1 = theSha1; 68 | self.repo = theRepo; 69 | 70 | return self; 71 | } 72 | 73 | - (id)initFromData: (NSData *)data repo: (GITRepo *)repo error: (NSError **)error { 74 | [self doesNotRecognizeSelector: _cmd]; 75 | [self release]; 76 | return nil; 77 | } 78 | 79 | - (void)dealloc { 80 | self.sha1 = nil; 81 | self.repo = nil; 82 | [super dealloc]; 83 | } 84 | 85 | - (NSString *)description { 86 | return [NSString stringWithFormat:@"<%@: %p sha1=%@>", NSStringFromClass([self class]), self, [self.sha1 unpackedString]]; 87 | } 88 | 89 | - (NSUInteger)hash { 90 | return [sha1 hash]; 91 | } 92 | - (BOOL)isEqual: (id)other { 93 | if ( !other ) return NO; // other is nil 94 | if ( other == self ) return YES; // pointers match? 95 | 96 | if ( [other isKindOfClass:[self class]] ) // Same class? 97 | return [self isEqualToObject:other]; 98 | return NO; // Definitely not then 99 | } 100 | - (BOOL)isEqualToObject: (GITObject *)rhs { 101 | if ( !rhs ) return NO; 102 | if ( self == rhs ) return YES; 103 | if ( type == rhs.type && [sha1 isEqualToObjectHash:[rhs sha1]] ) 104 | return YES; 105 | return NO; 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriterObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriterObject.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 06/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITObjectHash; 13 | 14 | /*! 15 | * GITPackIndexWriterObject is a class which encapsulates the data required 16 | * for writing entries into a PACK index file prior to writing the PACK file. 17 | */ 18 | @interface GITPackIndexWriterObject : NSObject { 19 | GITObjectHash *sha1; //!< Object hash of the object to store in the index 20 | NSUInteger offset; //!< Offset into the PACK file to find the object 21 | uint32_t crc32; //!< CRC32 of the data of the object 22 | } 23 | 24 | @property (retain) GITObjectHash *sha1; 25 | @property (assign) NSUInteger offset; 26 | @property (assign,setter=setCRC32:) uint32_t crc32; 27 | 28 | //! \name Creating and Initialising GITPackIndexWriterObject instances 29 | /*! 30 | * Create an autoreleased index writer object with the specified \a sha1 and \a offset. 31 | * 32 | * \param sha1 Object hash name of the object 33 | * \param offset Offset of the object into the PACK file 34 | * \return index writer object 35 | */ 36 | + (GITPackIndexWriterObject *)indexWriterObjectWithName: (GITObjectHash *)sha1 atOffset: (NSUInteger)offset; 37 | 38 | /*! 39 | * Initialises an index writer with the specified \a sha1 and \a offset. 40 | * 41 | * \param sha1 Object hash name of the object 42 | * \param offset Offset of the object into the PACK file 43 | * \return index writer object 44 | */ 45 | - (id)initWithName: (GITObjectHash *)sha1 atOffset: (NSUInteger)offset; 46 | 47 | //! \name Equality and Comparison 48 | /*! 49 | * Returns a Boolean value that indicates whether the receiver and a given object are equal. 50 | * 51 | * \param other The object to be compared to the receiver 52 | * \return YES if the receiver and other are equal, otherwise NO 53 | * \sa isEqualToIndexWriterObject: 54 | * \sa isEqualToObjectHash: 55 | */ 56 | - (BOOL)isEqual: (id)other; 57 | 58 | /*! 59 | * Returns a Boolean value that indicates whether the receiver and a given index write are equal. 60 | * 61 | * \param rhs The index writer with which to compare the receiver 62 | * \return YES if the receiver and rhs are equal, otherwise NO 63 | * \sa isEqual: 64 | * \sa isEqualToObjectHash: 65 | */ 66 | - (BOOL)isEqualToIndexWriterObject: (GITPackIndexWriterObject *)rhs; 67 | 68 | /*! 69 | * Returns a Boolean value that indicates whether the receiver and a given ObjectHash refer to the same objects. 70 | * 71 | * \param rhs The ObjectHash with which to compare the receiver 72 | * \return YES if the receiver and rhs are equal, otherwise NO 73 | * \sa isEqual: 74 | * \sa isEqualToIndexWriterObject: 75 | */ 76 | - (BOOL)isEqualToObjectHash: (GITObjectHash *)rhs; 77 | 78 | /*! 79 | * Returns an NSComparisonResult value that indicates whether the receiver is greater than, 80 | * equal to, or less than a given index writer object. 81 | * 82 | * \param obj The index writer object with which to compare the receiver. This value must 83 | * not be nil. If the value is nil, the behavior is undefined. 84 | * \return NSOrderedAscending if the value of \a obj is greater than the receiver’s, 85 | * NSOrderedSame if they’re equal, and NSOrderedDescending if the value of \a obj is less 86 | * than the receiver’s. 87 | */ 88 | - (NSComparisonResult)compare: (GITPackIndexWriterObject *)obj; 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Test/Unit/GITRefResolver.rb: -------------------------------------------------------------------------------- 1 | describe "GITRefResolver +resolverForRepo:" do 2 | before do 3 | @resolver = GITRefResolver.resolverForRepo(simple_repository.git_repo); 4 | end 5 | 6 | should "not be nil" do 7 | @resolver.should.not.be.nil 8 | end 9 | end 10 | 11 | describe "GITRefResolver -resolveRefWithName:" do 12 | before do 13 | @resolver = GITRefResolver.resolverForRepo(simple_repository.git_repo); 14 | end 15 | 16 | describe "HEAD" do 17 | before do 18 | @ref = @resolver.resolveRefWithName("HEAD") 19 | end 20 | 21 | should "not be nil" do 22 | @ref.should.not.be.nil 23 | end 24 | should "have name 'HEAD'" do 25 | @ref.name.should == 'HEAD' 26 | end 27 | should "have targetName 'ref: refs/heads/master'" do 28 | @ref.targetName.should == 'ref: refs/heads/master' 29 | end 30 | end 31 | 32 | describe "refs/heads/master" do 33 | before do 34 | @ref = @resolver.resolveRefWithName("refs/heads/master") 35 | end 36 | 37 | should "not be nil" do 38 | @ref.should.not.be.nil 39 | end 40 | should "have name 'refs/heads/master'" do 41 | @ref.name.should == 'refs/heads/master' 42 | end 43 | end 44 | 45 | describe "heads/master" do 46 | before do 47 | @ref = @resolver.resolveRefWithName("heads/master") 48 | end 49 | 50 | should "not be nil" do 51 | @ref.should.not.be.nil 52 | end 53 | should "have name 'refs/heads/master'" do 54 | @ref.name.should == 'refs/heads/master' 55 | end 56 | end 57 | 58 | describe "packed ref tags/v0.0.0" do 59 | before do 60 | @ref = @resolver.resolveRefWithName("tags/v0.0.0") 61 | end 62 | 63 | should "not be nil" do 64 | @ref.should.not.be.nil 65 | end 66 | should "have name 'refs/tags/v0.0.0'" do 67 | @ref.name.should == 'refs/tags/v0.0.0' 68 | end 69 | end 70 | end 71 | 72 | describe "GITRefResolver -allRefs" do 73 | before do 74 | @resolver = GITRefResolver.resolverForRepo(simple_repository.git_repo); 75 | @refs = @resolver.allRefs.map(&:name) 76 | end 77 | 78 | should "not be empty" do 79 | @refs.should.not.be.empty 80 | end 81 | should "contain 'refs/heads/master'" do 82 | @refs.should.include 'refs/heads/master' 83 | end 84 | should "contain 'refs/heads/another'" do 85 | @refs.should.include 'refs/heads/another' 86 | end 87 | should "contain 'refs/tags/v0.0.0'" do 88 | @refs.should.include 'refs/tags/v0.0.0' 89 | end 90 | end 91 | 92 | describe "GITRefResolver -headRefs" do 93 | before do 94 | @resolver = GITRefResolver.resolverForRepo(simple_repository.git_repo); 95 | @refs = @resolver.headRefs.map(&:name) 96 | end 97 | 98 | should "not be empty" do 99 | @refs.should.not.be.empty 100 | end 101 | should "contain 'refs/heads/master'" do 102 | @refs.should.include 'refs/heads/master' 103 | end 104 | should "contain 'refs/heads/another'" do 105 | @refs.should.include 'refs/heads/another' 106 | end 107 | end 108 | 109 | describe "GITRefResolver -tagRefs" do 110 | before do 111 | @resolver = GITRefResolver.resolverForRepo(simple_repository.git_repo); 112 | @refs = @resolver.tagRefs.map(&:name) 113 | end 114 | 115 | should "not be empty" do 116 | @refs.should.not.be.empty 117 | end 118 | should "contain 'refs/tags/v0.0.0'" do 119 | @refs.should.include 'refs/tags/v0.0.0' 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /Source/GITPackFileWriterVersionTwo.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFileWriterVersionTwo.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITPackFileWriter.h" 11 | #import "GITObject.h" 12 | 13 | 14 | @class GITPackIndexWriter; 15 | @interface GITPackFileWriterVersionTwo : GITPackFileWriter { 16 | char state; //!< Internal state of the writer 17 | CC_SHA1_CTX ctx; //!< CommonCrypto checksum context 18 | NSInteger offset; //!< Writing offset 19 | NSUInteger objectsWritten; //!< Count of objects written 20 | NSArray *objects; //!< Objects to PACK 21 | GITPackIndexWriter *indexWriter; //!< Index writer 22 | } 23 | 24 | @property (retain) GITPackIndexWriter *indexWriter; 25 | 26 | //! \name Output stream writers 27 | /*! 28 | * Writes and checksums the buffer into the stream. 29 | * 30 | * \param stream Output stream to write to 31 | * \param buffer Bytes to write to the stream 32 | * \param length Number of bytes to be written to the stream 33 | * \return number of bytes written 34 | * \sa stream:writeData: 35 | */ 36 | - (NSInteger)stream: (NSOutputStream *)stream write: (const uint8_t *)buffer maxLength: (NSUInteger)length; 37 | 38 | /*! 39 | * Writes and checksums the data into the stream. 40 | * 41 | * \param stream Output stream to write to 42 | * \param data Data to write to the stream 43 | * \return number of bytes written 44 | * \sa stream:write:maxLength: 45 | */ 46 | - (NSInteger)stream: (NSOutputStream *)stream writeData: (NSData *)data; 47 | 48 | /*! 49 | * Writes the checksum of the written data into the stream. 50 | * 51 | * \param stream Output stream to write the checksum to 52 | * \return number of bytes written 53 | * \sa stream:writeData: 54 | */ 55 | - (NSInteger)writeChecksumToStream: (NSOutputStream *)stream; 56 | 57 | //! \name Helper Methods 58 | /*! 59 | * Returns the data for the PACK file header. 60 | * 61 | * \param numberOfObjects Number of objects to include in the header 62 | * \return PACK header data 63 | */ 64 | - (NSData *)packedHeaderDataWithNumberOfObjects: (NSUInteger)numberOfObjects; 65 | 66 | /*! 67 | * Returns the data for the PACK object 68 | * 69 | * \param type Type of the object to be packed 70 | * \param data Data of the object to the packed 71 | * \return PACK object data 72 | */ 73 | - (NSData *)packedObjectDataWithType: (GITObjectType)type andData: (NSData *)data; 74 | 75 | //! \name Writer Methods 76 | /*! 77 | * Returns the next object from the objects array 78 | * 79 | * \return next GITObject 80 | * \sa writeNextObjectToStream: 81 | */ 82 | - (GITObject *)nextObject; 83 | 84 | /*! 85 | * Writes the header data to the stream. 86 | * 87 | * \param stream Output stream to write the header to 88 | * \return number of bytes written to the stream 89 | * \sa packedHeaderDataWithNumberOfObjects: 90 | * \sa stream:writeData: 91 | */ 92 | - (NSInteger)writeHeaderToStream: (NSOutputStream *)stream; 93 | 94 | /*! 95 | * Writes the PACK object to the stream 96 | * 97 | * \param stream Output stream to write the object to 98 | * \return number of bytes written to the stream 99 | * \sa nextObject: 100 | * \sa stream:writeData: 101 | */ 102 | - (NSInteger)writeNextObjectToStream: (NSOutputStream *)stream; 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Source/GITPackFileWriter.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFileWriter.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackFileWriter.h" 10 | #import "GITPackFileWriterPlaceholder.h" 11 | #import "GITRevList.h" 12 | 13 | 14 | @implementation GITPackFileWriter 15 | 16 | #pragma mark Class Cluster Methods 17 | + (id)alloc { 18 | if ([self isEqual:[GITPackFileWriter class]]) 19 | return [GITPackFileWriterPlaceholder alloc]; 20 | else return [super alloc]; 21 | } 22 | + (id)allocWithZone: (NSZone *)zone { 23 | if ([self isEqual:[GITPackFileWriter class]]) 24 | return [GITPackFileWriterPlaceholder allocWithZone:zone]; 25 | else return [super allocWithZone:zone]; 26 | } 27 | - (id)copyWithZone: (NSZone *)zone { 28 | return self; 29 | } 30 | 31 | #pragma mark Class Initialisers 32 | + (GITPackFileWriter *)packWriter { 33 | return [[[self alloc] initWithDefaultVersion] autorelease]; 34 | } 35 | + (GITPackFileWriter *)packWriterVersion: (NSUInteger)version { 36 | return [[[self alloc] initWithVersion:version error:NULL] autorelease]; 37 | } 38 | 39 | #pragma mark Initialisers 40 | - (id)initWithDefaultVersion { 41 | [self doesNotRecognizeSelector: _cmd]; 42 | [self release]; 43 | return nil; 44 | } 45 | - (id)initWithVersion: (NSUInteger)version error: (NSError **)error { 46 | [self doesNotRecognizeSelector: _cmd]; 47 | [self release]; 48 | return nil; 49 | } 50 | 51 | #pragma mark Add Objects to PACK 52 | - (void)addObjectsFromCommit: (GITCommit *)commit { 53 | return [self addObjectsFromRevList:[GITRevList revListWithCommit:commit]]; 54 | } 55 | 56 | #pragma mark PACK File Naming 57 | - (NSString *)fileName { 58 | return [NSString stringWithFormat:@"pack-%@.pack", [self name]]; 59 | } 60 | 61 | #pragma mark Un-implemented Instance Methods 62 | - (NSInteger)writeToStream: (NSOutputStream *)stream { 63 | [self doesNotRecognizeSelector: _cmd]; 64 | return -1; 65 | } 66 | - (GITPackIndexWriter *)indexWriter { 67 | [self doesNotRecognizeSelector: _cmd]; 68 | return nil; 69 | } 70 | - (void)setIndexWriter: (GITPackIndexWriter *)indexWriter { 71 | [self doesNotRecognizeSelector: _cmd]; 72 | } 73 | - (void)addObjectsFromRevList: (GITRevList *)revList { 74 | [self doesNotRecognizeSelector: _cmd]; 75 | } 76 | - (NSString *)name { 77 | [self doesNotRecognizeSelector: _cmd]; 78 | return nil; 79 | } 80 | 81 | #pragma mark NSRunLoop method 82 | - (void)writeToStream: (NSOutputStream *)stream inRunLoop: (NSRunLoop *)runLoop { 83 | [stream setDelegate:self]; 84 | [stream scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode]; 85 | [stream open]; 86 | } 87 | - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { 88 | switch ( eventCode ) { 89 | case NSStreamEventHasSpaceAvailable: 90 | [self writeToStream:(NSOutputStream *)stream]; 91 | break; 92 | case NSStreamEventErrorOccurred: 93 | // should really handle this 94 | break; 95 | } 96 | } 97 | 98 | #pragma mark Polling Method 99 | - (NSInteger)writeToStream: (NSOutputStream *)stream error: (NSError **)error { 100 | [stream open]; 101 | 102 | while ( [stream hasSpaceAvailable] ) { 103 | if ( [self writeToStream:stream] < 0 ) { 104 | if ( error ) *error = [stream streamError]; 105 | return -1; 106 | } 107 | } 108 | 109 | return 0; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /Source/GITRefResolver.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITRefResolver.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 18/09/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITRepo, GITRef; 13 | 14 | /*! 15 | * The \e GITRefResolver class provides methods to return GITRef 16 | * objects by their name. The class also provides methods to 17 | * obtain arrays of GITRef objects by \c tags, \c heads and \c remotes. 18 | */ 19 | @interface GITRefResolver : NSObject { 20 | GITRepo *repo; 21 | NSMutableDictionary *packedRefsCache; 22 | } 23 | 24 | @property (assign) GITRepo *repo; 25 | @property (retain) NSMutableDictionary *packedRefsCache; 26 | 27 | //! \name Creating and Initialising Ref Resolvers 28 | /*! 29 | * Creates and returns an autoreleased Ref Resolver for the 30 | * given repository. 31 | * 32 | * \param theRepo The repository the receiver will resolve with 33 | * \return resolver initialised with the specified repository 34 | * \sa initWithRepo: 35 | */ 36 | + (GITRefResolver *)resolverForRepo: (GITRepo *)theRepo; 37 | 38 | /*! 39 | * Creates and returns a Ref Resolver for the given repository. 40 | * 41 | * \param theRepo The repository the receiver will resolve with 42 | * \return resolver initialised with the specified repository 43 | */ 44 | - (id)initWithRepo: (GITRepo *)theRepo; 45 | 46 | //! \name Reference Resolving 47 | /*! 48 | * Returns a reference by resolving the specified name. 49 | * 50 | * Resolves references in the following order: 51 | * \li \c refs/ 52 | * \li \c refs/tags/ 53 | * \li \c refs/heads/ 54 | * \li \c refs/remotes/ 55 | * 56 | * \param theName Name of the reference to resolve 57 | * \return Reference resolved from the specified name, or nil 58 | * if the reference cannot be found or if an error occurred 59 | * \sa resolveRefWithName:error: 60 | */ 61 | - (GITRef *)resolveRefWithName: (NSString *)theName; 62 | 63 | /*! 64 | * Returns a reference by resolving the specified name. 65 | * 66 | * Resolves references in the following order: 67 | * \li \c refs/ 68 | * \li \c refs/tags/ 69 | * \li \c refs/heads/ 70 | * \li \c refs/remotes/ 71 | * 72 | * \param theName Name of the reference to resolve 73 | * \param theError NSError describing the error that occurred 74 | * \return Reference resolved from the specified name, or nil 75 | * if the reference cannot be found or if an error occurred 76 | */ 77 | - (GITRef *)resolveRefWithName: (NSString *)theName error: (NSError **)theError; 78 | 79 | //! \name Gathering References 80 | /*! 81 | * Returns an array of all the references in the receivers repository. 82 | * 83 | * \return array of all the references in the receivers repository 84 | */ 85 | - (NSArray *)allRefs; 86 | 87 | /*! 88 | * Returns an array of all the \c tag references in the receivers repository. 89 | * 90 | * \return array of all the \c tag references in the receivers repository 91 | * \sa allRefs 92 | */ 93 | - (NSArray *)tagRefs; 94 | 95 | /*! 96 | * Returns an array of all the \c head references in the receivers repository. 97 | * 98 | * \return array of all the \c head references in the receivers repository 99 | * \sa allRefs 100 | */ 101 | - (NSArray *)headRefs; 102 | 103 | /*! 104 | * Returns an array of all the \c head references in the receivers repository. 105 | * 106 | * \return array of all the \c head references in the receivers repository 107 | * \sa allRefs 108 | */ 109 | - (NSArray *)remoteRefs; 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Vendor/OmniGroup/OmniBase/OBError.h: -------------------------------------------------------------------------------- 1 | // Copyright 1997-2005, 2007-2009 Omni Development, Inc. All rights reserved. 2 | // 3 | // This software may only be used and reproduced according to the 4 | // terms in the file OmniSourceLicense.html, which should be 5 | // distributed with this project and can also be found at 6 | // . 7 | // 8 | // $Id$ 9 | 10 | #if defined(__cplusplus) 11 | extern "C" { 12 | #endif 13 | 14 | extern NSString * const OBErrorDomain; 15 | extern NSString * const OBFileNameAndNumberErrorKey; 16 | 17 | enum { 18 | OBErrorChained = 1, /* skip code zero since that is often defined to be 'no error' */ 19 | }; 20 | 21 | extern NSError *_OBWrapUnderlyingError(NSError *underlyingError, NSString *domain, int code, const char *fileName, unsigned int line, NSString *firstKey, ...) NS_REQUIRES_NIL_TERMINATION; 22 | 23 | // Clang complains if we have a function that takes NSError ** without returning BOOL/pointer. 24 | // Bail on a NULL outError. Some Foundation code on 10.4 would crash if you did this, but on 10.5, many methods are documented to allow it. So let's allow it also. 25 | #define _OBError(outError, domain, code, fileName, line, firstKey, ...) do { \ 26 | NSError **_outError = (outError); \ 27 | if (_outError) \ 28 | *_outError = _OBWrapUnderlyingError(*_outError, domain, code, fileName, line, firstKey, ## __VA_ARGS__); \ 29 | } while(0) 30 | 31 | // Stacks another error on the input that simple records the calling code. This can help establish the chain of failure when a callsite just fails because something else failed, without the overhead of adding another error code for that specific site. 32 | #define OBChainError(error) _OBError(error, OBErrorDomain, OBErrorChained, __FILE__, __LINE__, nil) 33 | extern NSError *OBFirstUnchainedError(NSError *error); 34 | 35 | #ifdef OMNI_BUNDLE_IDENTIFIER 36 | // It is expected that -DOMNI_BUNDLE_IDENTIFIER=@"com.foo.bar" will be set when building your code. Build configurations make this easy since you can set it in the target's configuration and then have your Other C Flags have -DOMNI_BUNDLE_IDENTIFIER=@\"$(OMNI_BUNDLE_IDENTIFIER)\" and also use $(OMNI_BUNDLE_IDENTIFIER) in your Info.plist instead of duplicating it. 37 | #define OBError(error, code, description) _OBError(error, OMNI_BUNDLE_IDENTIFIER, code, __FILE__, __LINE__, NSLocalizedDescriptionKey, description, nil) 38 | #define OBErrorWithInfo(error, code, ...) _OBError(error, OMNI_BUNDLE_IDENTIFIER, code, __FILE__, __LINE__, ## __VA_ARGS__) 39 | #endif 40 | 41 | // Unlike the other routines in this file, but like all the other Foundation routines, this takes its key-value pairs with each value followed by its key. The disadvantage to this is that you can't easily have runtime-ignored values (the nil value is a terminator rather than being skipped). 42 | extern NSError *_OBErrorWithErrnoObjectsAndKeys(int errno_value, const char *function, NSString *argument, NSString *localizedDescription, ...) NS_REQUIRES_NIL_TERMINATION; 43 | #define OBErrorWithErrnoObjectsAndKeys(outError, errno_value, function, argument, localizedDescription, ...) do { \ 44 | NSError **_outError = (outError); \ 45 | OBASSERT(!_outError || !*_outError); /*no underlying error support*/ \ 46 | if (_outError) \ 47 | *_outError = _OBErrorWithErrnoObjectsAndKeys(errno_value, function, argument, localizedDescription, ## __VA_ARGS__); \ 48 | } while(0) 49 | 50 | #define OBErrorWithErrno(error, errno_value, function, argument, localizedDescription) OBErrorWithErrnoObjectsAndKeys(error, errno_value, function, argument, localizedDescription, nil) 51 | 52 | #if defined(__cplusplus) 53 | } // extern "C" 54 | #endif 55 | 56 | -------------------------------------------------------------------------------- /Source/GITCommitEnumerator.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITCommitEnumerator.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 06/02/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! \name Constants 12 | /*! 13 | * Flags for indicating enumeration methods 14 | */ 15 | typedef enum { 16 | GITCommitEnumeratorBreadthFirstMode, //!< A flag indicating the enumerator should use breadth first traversal 17 | GITCommitEnumeratorDepthFirstMode, //!< A flag indicating the enumerator should use depth first traversal 18 | } GITCommitEnumeratorMode; 19 | 20 | @class GITCommit; 21 | 22 | /*! 23 | * An NSEnumerator subclass which provides the ability to iterate or collect the 24 | * series of parents of a given commit. 25 | * 26 | * The enumerator provides two distinct enumeration modes. The first and default mode 27 | * is breadth first traversal and is specified via GITCommitEnumeratorBreadthFirstMode 28 | * passed as the mode argument to either +enumeratorFromCommit:mode: or 29 | * -initFromCommit:mode: method. The second enumeration mode performs a depth first 30 | * traversal and is specified via GITCommitEnumeratorDepthFirstMode. 31 | * 32 | * \internal 33 | * Modes cannot be mixed, most specifically because the queue is used 34 | * in different ways between the Breadth and Depth traversal algorithms. 35 | */ 36 | @interface GITCommitEnumerator : NSEnumerator { 37 | GITCommit *head; //!< Enumerator starting point 38 | GITCommitEnumeratorMode mode; //!< Mode of enumeration 39 | NSMutableArray *queue; //!< Queue of commit names 40 | NSMutableArray *merges; //!< DFS list of revisit points 41 | NSMutableSet *visited; //!< Stores "grey" commits 42 | BOOL firstPass; //!< Flag indicating if -nextObject is going through its first call 43 | } 44 | 45 | //! \name Creating and Initialising Enumerators 46 | /*! 47 | * Creates and returns an enumerator from the given commit \a head. 48 | * 49 | * The enumerator is created with the default enumeration mode. The default enumeration 50 | * mode is GITCommitEnumeratorBreadthFirstMode indicating the use of breadth first 51 | * traversal algorithm. 52 | * 53 | * \param head Commit object from which the receiver will enumerate from 54 | * \return An enumerator which will start from the \a head using breadth first traversal 55 | * \sa enumeratorFromCommit:mode: 56 | * \sa initFromCommit:mode: 57 | */ 58 | + (GITCommitEnumerator *)enumeratorFromCommit: (GITCommit *)head; 59 | 60 | /*! 61 | * Creates and returns an enumerator from the given commit \a head using the provided enumeration \a mode. 62 | * 63 | * \param head Commit object from which the receiver will enumerate from 64 | * \param mode A GITCommitEnumeratorMode flag indicating the type of enumeration to be used 65 | * \return An enumerator object which will start from the \a head using the specified \a mode 66 | * \sa enumeratorFromCommit: 67 | * \sa initFromCommit:mode: 68 | */ 69 | + (GITCommitEnumerator *)enumeratorFromCommit: (GITCommit *)head mode: (GITCommitEnumeratorMode)mode; 70 | 71 | /*! 72 | * Initialises a newly allocated enumerator object initialised with the given commit \a head and \a mode. 73 | * 74 | * \param head Commit object from which the receiver will enumerate from 75 | * \param mode A GITCommitEnumeratorMode flag indicating the type of enumeration to be used 76 | * \return An enumerator object initialised with the given commit \a head and \a mode 77 | * \sa enumeratorFromCommit: 78 | * \sa enumeratorFromCommit:mode: 79 | */ 80 | - (id)initFromCommit: (GITCommit *)head mode: (GITCommitEnumeratorMode)mode; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Source/GITRevList.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITRevList.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 11/07/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITRepo, GITCommit, GITGraph; 13 | 14 | /*! 15 | * The GITRevList class lists commit objects in reverse chronological order. A subset 16 | * of the commit history can be excluded or negated from the list by calling -subtractDescendentsFromCommit: 17 | * which will remove all commits reachable from the specified commit from the list. 18 | * 19 | * Sorted lists of commits are provided through one of the -arrayOfCommitsSortedBy* methods. 20 | */ 21 | @interface GITRevList : NSObject { 22 | GITRepo *repo; 23 | GITGraph *graph; 24 | NSMutableArray *excluded; 25 | } 26 | 27 | //! \name Creating and Initialising Rev-Lists 28 | /*! 29 | * Creates and returns a rev list object with the history from the \a head commit. 30 | * 31 | * \param head Commit object to list from 32 | * \return New rev list object with the history of the \a head commit. 33 | */ 34 | + (GITRevList *)revListWithCommit: (GITCommit *)head; 35 | 36 | /*! 37 | * Initializes and returns a rev list object with history from the \a head commit. 38 | * 39 | * \param head Commit object to list from 40 | * \return Rev list object with the history of the \a head commit. 41 | */ 42 | - (id)initWithCommit: (GITCommit *)head; 43 | 44 | //! \name Modifying Rev-Lists 45 | /*! 46 | * Removes commits from the receiver which are in the history of the given \a tail commit. 47 | */ 48 | - (void)subtractDescendentsFromCommit: (GITCommit *)tail; 49 | 50 | //! \name Sorted Lists from Rev-Lists 51 | /*! 52 | * Returns an array of commit objects sorted by their commit date. 53 | * 54 | * The result of this method is equivalent to git rev-list. 55 | * 56 | * \return array of commits sorted by date 57 | */ 58 | - (NSArray *)arrayOfCommitsSortedByDate; 59 | 60 | /*! 61 | * Returns an array of commit objects sorted by topology. 62 | * 63 | * The result of this method is equivalent to git rev-list --topo-order. 64 | * 65 | * \return array of commits sorted by topology 66 | */ 67 | - (NSArray *)arrayOfCommitsSortedByTopology; 68 | 69 | /*! 70 | * Returns an array of commit objects sorted by topology and commit date. 71 | * 72 | * The result of this method is equivalent to git rev-list --date-order. 73 | * 74 | * \return array of commits sorted by topology and date 75 | */ 76 | - (NSArray *)arrayOfCommitsSortedByTopologyAndDate; 77 | 78 | /*! 79 | * Returns an array of the objects which are reachable from the head of the receiver. 80 | * 81 | * This is primarily used when collecting the list of objects to include in a PACK file 82 | * prior to generation. 83 | * 84 | * \return Array of objects reachable from the head of the receiver. 85 | */ 86 | - (NSArray *)arrayOfReachableObjects; 87 | 88 | /*! 89 | * Returns an array of the objects and tags which are reachable from the head of the receiver. 90 | * 91 | * \return Array of objects and tags reachable from the head of the receiver. 92 | */ 93 | - (NSArray *)arrayOfReachableObjectsAndTags; 94 | 95 | /*! 96 | * Returns an array of the objects and tags which are reachable from the head of the receiver. 97 | * 98 | * \param tags Array of tags to be considered for inclusion in the resulting array. This should 99 | * be an inclusive list of all tags which might be reachable from the head of the receiver. 100 | * \return Array of objects and tags reachable from the head of the receiver. 101 | */ 102 | - (NSArray *)arrayOfReachableObjectsAndTags: (NSArray *)tags; 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Source/GITObject+Parsing.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITObject+Parsing.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 13/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITObject+Parsing.h" 10 | #import "GITObjectHash.h" 11 | 12 | 13 | @implementation GITObject (Parsing) 14 | 15 | - (GITObjectHash *)newObjectHashWithObjectRecord: (parsingRecord)record bytes:(const char **)bytes { 16 | const char *start; 17 | NSUInteger len; 18 | 19 | if ( !parseObjectRecord(bytes, record, &start, &len) ) 20 | return nil; 21 | return [[GITObjectHash alloc] initWithBytes:start length:len]; 22 | } 23 | 24 | - (NSString *)newStringWithObjectRecord: (parsingRecord)record bytes: (const char **)bytes { 25 | return [self newStringWithObjectRecord:record bytes:bytes encoding:NSUTF8StringEncoding]; 26 | } 27 | 28 | - (NSString *)newStringWithObjectRecord: (parsingRecord)record bytes: (const char **)bytes encoding: (NSStringEncoding)encoding { 29 | const char *start; 30 | NSString *string; 31 | NSUInteger len; 32 | 33 | if ( !parseObjectRecord(bytes, record, &start, &len) ) 34 | return nil; 35 | 36 | string = [[NSString alloc] initWithBytes:start length:len encoding:encoding]; 37 | if ( string == nil ) 38 | string = [[NSString alloc] initWithBytes:start length:len encoding:NSASCIIStringEncoding]; 39 | 40 | return string; 41 | } 42 | 43 | @end 44 | 45 | BOOL parseObjectRecord(const char **buffer, parsingRecord record, const char **matchStart, NSUInteger *matchLength) { 46 | const char *buf = *buffer; 47 | 48 | // If we have an initial pattern then see if we've got it 49 | if ( record.patternLen > 0 && memcmp(buf, record.startPattern, record.patternLen) ) 50 | return NO; 51 | 52 | const char *start; // Initialise start and determine start position 53 | if ( record.startLen > 0 ) { start = buf + record.startLen; } // offset the buffer by the indicated start position if > 0 54 | else { start = buf + record.patternLen; } // otherwise offset by the pattern length 55 | 56 | const char *end = start; 57 | if ( (record.startLen >= 0) && (record.matchLen > 0) ) { // If we know the length of the match, then set the end to it 58 | end = start + record.matchLen; 59 | } else { // Otherwise loop through the characters until we find endChar 60 | while ( *end++ != record.endChar ) { /* no-op */ } // TODO: BRC: Add bounds checking 61 | 62 | if ( record.startLen < 0 ) { // If negative startLen, move the start pointer 63 | NSUInteger len = end - start; // Work out how far from the start the end is 64 | start += len + record.startLen; // Move the start to .startLen bytes before the end 65 | } 66 | 67 | --end; // end should point to the delimiting char 68 | } 69 | 70 | // check that end = endChar, otherwise there was a parsing problem 71 | if ( record.endChar != -1 && end[0] != record.endChar ) { 72 | NSLog(@"end delimiter (%c) does not match end char:%c\n", record.endChar, end[0]); 73 | return NO; 74 | } 75 | 76 | // set matchLen 77 | NSUInteger matchLen = record.matchLen; 78 | if ( record.matchLen == 0 ) 79 | matchLen = end - start; 80 | 81 | if ( matchStart != NULL ) 82 | *matchStart = start; 83 | if ( matchLength != NULL ) 84 | *matchLength = matchLen; 85 | 86 | *buffer = end; 87 | if ( record.endChar != -1 ) 88 | *buffer += 1; // skip over the delimiting char 89 | return YES; 90 | } 91 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriterVersionTwo.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriterVersionTwo.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITPackIndexWriter.h" 11 | 12 | 13 | @interface GITPackIndexWriterVersionTwo : GITPackIndexWriter { 14 | char state; //!< Internal state of the writer 15 | CC_SHA1_CTX ctx; //!< CommonCrypto checksum context 16 | uint32_t fanoutTable[256]; //!< Fanout table 17 | NSMutableArray *objects; //!< Indexed objects 18 | NSInteger objectsWritten; //!< Count of objects written 19 | NSMutableData *extOffsets; //!< Extended offsets data 20 | NSData *packChecksum; //!< PACK file checksum 21 | } 22 | 23 | /*! 24 | * Returns the fanout table array 25 | * 26 | * \return fanout table array 27 | */ 28 | - (uint32_t *)fanoutTable; 29 | 30 | //! \name Output stream writers 31 | /*! 32 | * Writes and checksums the buffer into the stream. 33 | * 34 | * \param stream Output stream to write to 35 | * \param buffer Bytes to write to the stream 36 | * \param length Number of bytes to be written to the stream 37 | * \return number of bytes written 38 | * \sa stream:writeData: 39 | */ 40 | - (NSInteger)stream: (NSOutputStream *)stream write: (const uint8_t *)buffer maxLength: (NSUInteger)length; 41 | 42 | /*! 43 | * Writes and checksums the data into the stream. 44 | * 45 | * \param stream Output stream to write to 46 | * \param data Data to write to the stream 47 | * \return number of bytes written 48 | * \sa stream:write:maxLength: 49 | */ 50 | - (NSInteger)stream: (NSOutputStream *)stream writeData: (NSData *)data; 51 | 52 | /*! 53 | * Writes the checksum of the written data into the stream. 54 | * 55 | * \param stream Output stream to write the checksum to 56 | * \return number of bytes written 57 | * \sa stream:writeData: 58 | */ 59 | - (NSInteger)writeChecksumToStream: (NSOutputStream *)stream; 60 | 61 | /*! 62 | * Returns the index header data. 63 | * 64 | * \return index header data 65 | */ 66 | - (NSData *)indexHeaderData; 67 | 68 | //! \name Writer Methods 69 | /*! 70 | * Writes PACK Index header to the stream. 71 | * 72 | * \param stream Output stream to write the object entry to 73 | * \return number of bytes written 74 | */ 75 | - (NSInteger)writeHeaderToStream: (NSOutputStream *)stream; 76 | 77 | /*! 78 | * Writes the object name to the stream. 79 | * 80 | * \param stream Output stream to write the object entry to 81 | * \return number of bytes written 82 | */ 83 | - (NSInteger)writeObjectNameToStream: (NSOutputStream *)stream; 84 | 85 | /*! 86 | * Writes the CRC32 values to the stream. 87 | * 88 | * \param stream Output stream to write the object entry to 89 | * \return number of bytes written 90 | */ 91 | - (NSInteger)writeCRC32ValueToStream: (NSOutputStream *)stream; 92 | 93 | /*! 94 | * Writes the offset values to the stream. 95 | * 96 | * \param stream Output stream to write the object entry to 97 | * \return number of bytes written 98 | */ 99 | - (NSInteger)writeOffsetValueToStream: (NSOutputStream *)stream; 100 | 101 | /*! 102 | * Writes the extended offsets to the stream. 103 | * 104 | * \param stream Output stream to write the object entry to 105 | * \return number of bytes written 106 | */ 107 | - (NSInteger)writeExtendedOffsetsToStream: (NSOutputStream *)stream; 108 | 109 | /*! 110 | * Writes the PACK file checksum to the stream. 111 | * 112 | * \param stream Output stream to write the object entry to 113 | * \return number of bytes written 114 | */ 115 | - (NSInteger)writePackChecksumToStream: (NSOutputStream *)stream; 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /Source/GITCommit.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITCommit.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 13/12/2009. 6 | // Copyright 2009 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GITObject.h" 11 | 12 | 13 | @class GITTree, GITObjectHash, GITActor, GITDateTime; 14 | 15 | /*! 16 | * This class represents \e Commit objects in a git repository. 17 | * 18 | * Commit objects are markers of the state of a repository at a 19 | * specific moment in time, they reference trees which define the 20 | * file system structure of a repository at a given point. 21 | * 22 | * Commit objects look like the following, the parent may be repeated. 23 | * \code 24 | * tree 80655da8d80aaaf92ce5357e7828dc09adb00993 25 | * parent d8fd39d0bbdd2dcf322d8b11390a4c5825b11495 26 | * author A. Developer 444123000 +0000 27 | * committer A. Developer 444123000 +0000 28 | * 29 | * A commit message meant to describe what this commit is all about 30 | * \endcode 31 | */ 32 | @interface GITCommit : GITObject { 33 | NSArray *parents; //!< Array of GITCommit parents 34 | NSArray *parentShas; //!< Array of GITObjectHash objects of the parents 35 | 36 | GITTree *tree; //!< The tree object the commit references 37 | GITObjectHash *treeSha1; //!< The hash of the tree object the commit references 38 | 39 | GITActor *author; //!< Author of the commit 40 | GITActor *committer; //!< Committer of the commit, different from author if the commit was created by patching 41 | 42 | GITDateTime *authorDate; //!< Date the commit was created 43 | GITDateTime *committerDate; //!< Date the commit was committed to the repository via patching 44 | 45 | NSString *message; //!< Commit message 46 | 47 | NSData *cachedData; //!< Copy of the data of the object, used for lazy object loading 48 | 49 | NSTimeInterval nodeSortTimeInterval; 50 | } 51 | 52 | //! \name Properties 53 | @property (copy) NSArray *parentShas; 54 | @property (copy) NSArray *parents; 55 | @property (retain) GITTree *tree; 56 | @property (retain) GITActor *author; 57 | @property (retain) GITActor *committer; 58 | @property (retain) GITDateTime *authorDate; 59 | @property (retain) GITDateTime *committerDate; 60 | @property (copy) NSString *message; 61 | 62 | //! \name Creating and Initialising Commits 63 | /*! 64 | * Creates and returns a commit from the \a data. 65 | * 66 | * The \a data content is parsed to extract the information about the commit such 67 | * as the referenced tree, parents, author, committer, dates and the commit message. 68 | * 69 | * \param data The data describing the commit 70 | * \param objectHash The SHA1 hash of the receiver 71 | * \param repo The repository the commit is a member of 72 | * \param error NSError describing the error that occurred 73 | * \return A commit object from the \a data 74 | */ 75 | + (GITCommit *)commitFromData: (NSData *)data sha1: (GITObjectHash *)objectHash repo: (GITRepo *)repo error: (NSError **)error; 76 | 77 | //! \name Properties 78 | /*! 79 | * Returns the last SHA1 in the list of parent commits. 80 | * 81 | * \return SHA1 of the parent commit 82 | * \sa parentShas 83 | */ 84 | - (NSString *)parentSha1; 85 | 86 | //! \name Flags 87 | /*! 88 | * Returns YES if the receiver is a merge point, NO if it has only one parent. 89 | * 90 | * \return YES if the receiver is a merge point, NO if only one parent 91 | * \sa parentShas 92 | */ 93 | - (BOOL)isMerge; 94 | 95 | /*! 96 | * Returns YES if the receiver if the first commit, NO otherwise. 97 | * 98 | * \return YES if the receiver is the first commit, NO otherwise. 99 | * \sa parentShas 100 | */ 101 | - (BOOL)isInitial; 102 | 103 | - (NSNumber *)nodeSortTimeInterval; 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /Source/Category/NSData/NSData+CRC32.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+CRC32.m 3 | // 4 | // Created by Geoffrey Garside on 08/11/2010. 5 | // Believed to be Public Domain. 6 | // 7 | // Methods extracted from source given at 8 | // http://www.cocoadev.com/index.pl?NSDataCategory 9 | // 10 | 11 | #import "NSData+CRC32.h" 12 | 13 | 14 | @implementation NSData (CRC32) 15 | 16 | static const unsigned long crc32table[] = 17 | { 18 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 19 | 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 20 | 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 21 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 22 | 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 23 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 24 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 25 | 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 26 | 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 27 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 28 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 29 | 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 30 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 31 | 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 32 | 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 33 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 34 | 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 35 | 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 36 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 37 | 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 38 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 39 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 40 | 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 41 | 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 42 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 43 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 44 | 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 45 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 46 | 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 47 | 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 48 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 49 | 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 50 | }; 51 | 52 | - (uint32_t)crc32 53 | { 54 | unsigned int crcval; 55 | unsigned int x, y; 56 | const void *bytes; 57 | unsigned int max; 58 | 59 | bytes = [self bytes]; 60 | max = [self length]; 61 | crcval = 0xffffffff; 62 | for (x = 0, y = max; x < y; x++) { 63 | crcval = ((crcval >> 8) & 0x00ffffff) ^ crc32table[(crcval ^ (*((unsigned char *)bytes + x))) & 0xff]; 64 | } 65 | 66 | return (crcval ^ 0xffffffff); 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /Source/GITPackFileWriter.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackFileWriter.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @class GITPackIndexWriter, GITRevList, GITCommit; 13 | 14 | /*! 15 | * GITPackFileWriter is a class cluster which provides the ability to 16 | * generate PACK files from a given set of objects. 17 | */ 18 | @interface GITPackFileWriter : NSObject { } 19 | 20 | //! \name Creating and Initialising GITPackFileWriter objects 21 | /*! 22 | * Creates an autoreleased PACK file writer for the default PACK file version. 23 | * 24 | * \return PACK file writer for the default version 25 | */ 26 | + (GITPackFileWriter *)packWriter; 27 | 28 | /*! 29 | * Creates an autoreleased PACK file writer for the specified \a version. 30 | * 31 | * \param version Version of writer to create 32 | * \return PACK file writer with the specified \a version, nil if \a version is unsupported. 33 | */ 34 | + (GITPackFileWriter *)packWriterVersion: (NSUInteger)version; 35 | 36 | /*! 37 | * Initialises a PACK file writer for the default PACK file version. 38 | * 39 | * \return PACK file writer for the default version 40 | */ 41 | - (id)initWithDefaultVersion; 42 | 43 | /*! 44 | * Initialises a PACK file writer for the specified \a version. 45 | * 46 | * \param version Version of the writer to create 47 | * \param[out] error NSError describing the error that occurred 48 | * \return PACK file writer for the specified \a version, or nil on error 49 | */ 50 | - (id)initWithVersion: (NSUInteger)version error: (NSError **)error; 51 | 52 | //! \name Adding to the PACK file 53 | /*! 54 | * Add the objects reachable from the provided \a revList to the reciever. 55 | * 56 | * \param revList Rev list of objects to add to the receiver. 57 | */ 58 | - (void)addObjectsFromRevList: (GITRevList *)revList; 59 | 60 | /*! 61 | * Add the objects reachable from the provided \a commit to the receiver. 62 | * 63 | * \param commit Commit object to reach objects from. 64 | * \sa addObjectsFromRevList: 65 | */ 66 | - (void)addObjectsFromCommit: (GITCommit *)commit; 67 | 68 | //! \name PACK file naming 69 | /*! 70 | * Returns the name of the receiver derived from the objects to be packed. 71 | * 72 | * \return SHA1 string name of the receiver derived from the objects to the packed 73 | */ 74 | - (NSString *)name; 75 | 76 | /*! 77 | * Returns the file name of the receiver. 78 | * 79 | * \return File name of the receiver. 80 | * \sa name 81 | */ 82 | - (NSString *)fileName; 83 | 84 | //! \name Writing the PACK File 85 | /*! 86 | * Writes the PACK file contents to the provided \a stream in the NSRunLoop specified. 87 | * 88 | * \todo We need a way of getting any stream errors sent back to the client 89 | * \param stream Output stream to write the PACK index data to 90 | * \param runLoop NSRunLoop to schedule the writing in 91 | */ 92 | - (void)writeToStream: (NSOutputStream *)stream inRunLoop: (NSRunLoop *)runLoop; 93 | 94 | /*! 95 | * Writes the PACK file contents to the provided \a steam. 96 | * 97 | * This method using Polling rather than run-loop scheduling to perform the writing. 98 | * 99 | * \param stream Output stream to write the PACK index data to 100 | * \param[out] error NSError describing any error which occurred 101 | * \return 0 if writing was successful, -1 if an error occurred. 102 | */ 103 | - (NSInteger)writeToStream: (NSOutputStream *)stream error: (NSError **)error; 104 | 105 | //! \name PACK Index Writer 106 | /*! 107 | * Returns the PACK index writer of the receiver. 108 | * 109 | * \return PACK index writer of the receiver 110 | */ 111 | - (GITPackIndexWriter *)indexWriter; 112 | 113 | /*! 114 | * Sets the provided \a indexWriter on the receiver. 115 | * 116 | * \param indexWriter PACK index writer for the receiver to populate when it writes itself. 117 | */ 118 | - (void)setIndexWriter: (GITPackIndexWriter *)indexWriter; 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /Source/GITDateTime.h: -------------------------------------------------------------------------------- 1 | // 2 | // GITDateTime.h 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 10/01/2010. 6 | // Copyright 2010 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /*! 13 | * Git objects specify their creation and other dates along with the associated 14 | * time zone. 15 | * 16 | * This class tracks these pieces of information and also aids in 17 | * creation of the date time objects from the UNIX timestamp and time zone offsets 18 | * used in stored git objects. 19 | */ 20 | @interface GITDateTime : NSObject { 21 | NSDate *date; //!< Date 22 | NSTimeZone *timeZone; //!< Time Zone 23 | } 24 | 25 | //! \name Properties 26 | @property (copy) NSDate *date; 27 | @property (copy) NSTimeZone *timeZone; 28 | 29 | //! \name Creating and Initialising DateTimes 30 | /*! 31 | * Creates and returns a date time object for the \a date in the local time zone. 32 | * 33 | * \param date Date object giving the date and time for the receiver 34 | * \return date time object 35 | * \sa initWithDate: 36 | */ 37 | + (GITDateTime *)dateTimeWithDate: (NSDate *)date; 38 | 39 | /*! 40 | * Creates and returns a date time object for the \a date in the given \a timeZone. 41 | * 42 | * \param date Date object giving the date and time for the receiver 43 | * \param timeZone time zone of the date and time 44 | * \return date time object 45 | * \sa initWithDate:timeZone: 46 | */ 47 | + (GITDateTime *)dateTimeWithDate: (NSDate *)date timeZone: (NSTimeZone *)timeZone; 48 | 49 | /*! 50 | * Creates and returns a date time object from the UNIX timestamp \a seconds and the string \a offset. 51 | * 52 | * The \a offset is normally of the form "+0100" or "-0100", the \a seconds counted from the start of 53 | * the UNIX Epoch which is the first of January 1970 rather than the Mac OS X Reference Date which is 54 | * the first of January 2001. 55 | * 56 | * \param seconds UNIX timestamp specifying the date 57 | * \param offset Zone offset from Greenwich in "+/-HHMM" format 58 | * \return date time object 59 | * \sa initWithTimestamp:timeZoneOffset: 60 | */ 61 | + (GITDateTime *)dateTimeWithTimestamp: (NSTimeInterval)seconds timeZoneOffset: (NSString *)offset; 62 | 63 | /*! 64 | * Creates and returns a date time object for the \a date in the local time zone. 65 | * 66 | * \param date Date object giving the date and time for the receiver 67 | * \return date time object 68 | * \sa dateTimeWithDate: 69 | * \sa initWithDate:timeZone: 70 | */ 71 | - (id)initWithDate: (NSDate *)date; 72 | 73 | /*! 74 | * Creates and returns a date time object for the \a date in the given \a timeZone. 75 | * 76 | * \param date Date object giving the date and time for the receiver 77 | * \param timeZone time zone of the date and time 78 | * \return date time object 79 | */ 80 | - (id)initWithDate: (NSDate *)date timeZone: (NSTimeZone *)timeZone; 81 | 82 | /*! 83 | * Creates and returns a date time object from the UNIX timestamp \a seconds and the string \a offset. 84 | * 85 | * The \a offset is normally of the form "+0100" or "-0100", the \a seconds counted from the start of 86 | * the UNIX Epoch which is the first of January 1970 rather than the Mac OS X Reference Date which is 87 | * the first of January 2001. 88 | * 89 | * \param seconds UNIX timestamp specifying the date 90 | * \param offset Zone offset from Greenwich in "+/-HHMM" format 91 | * \return date time object 92 | * \sa initWithDate:timeZone: 93 | */ 94 | - (id)initWithTimestamp: (NSTimeInterval)seconds timeZoneOffset: (NSString *)offset; 95 | 96 | /*! 97 | * Returns a string representation of the receiver with the given format. 98 | * 99 | * \param format The date format for the receiver. See Data Formatting Programming Guide for Cocoa for a 100 | * list of the conversion specifiers permitted in date format strings. 101 | * \return string representation of the receiver 102 | * \sa http://devworld.apple.com/mac/library/documentation/Cocoa/Conceptual/DataFormatting/DataFormatting.html 103 | */ 104 | - (NSString *)stringWithFormat: (NSString *)format; 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /Source/GITPackIndexWriterVersionOne.m: -------------------------------------------------------------------------------- 1 | // 2 | // GITPackIndexWriterVersionOne.m 3 | // Git.framework 4 | // 5 | // Created by Geoff Garside on 05/02/2011. 6 | // Copyright 2011 Geoff Garside. All rights reserved. 7 | // 8 | 9 | #import "GITPackIndexWriterVersionOne.h" 10 | #import "GITPackIndexWriter+Shared.h" 11 | #import "GITPackIndexWriterObject.h" 12 | #import "GITObjectHash.h" 13 | 14 | 15 | @interface GITPackIndexWriterVersionOne () 16 | @property (retain) NSMutableArray *objects; 17 | @property (copy) NSData *packChecksum; 18 | @end 19 | 20 | @implementation GITPackIndexWriterVersionOne 21 | @synthesize objects, packChecksum; 22 | 23 | - (id)init { 24 | if ( ![super init] ) 25 | return nil; 26 | 27 | state = 0; 28 | objectsWritten = 0; 29 | CC_SHA1_Init(&ctx); 30 | memset(fanoutTable, 0, sizeof(fanoutTable)); 31 | self.objects = [NSMutableArray array]; 32 | 33 | return self; 34 | } 35 | 36 | - (void)dealloc { 37 | self.objects = nil; 38 | self.packChecksum = nil; 39 | [super dealloc]; 40 | } 41 | 42 | - (uint32_t *)fanoutTable { 43 | return fanoutTable; 44 | } 45 | 46 | #pragma mark Checksumming writer methods 47 | - (NSInteger)stream: (NSOutputStream *)stream write: (const uint8_t *)buffer maxLength: (NSUInteger)length { 48 | CC_SHA1_Update(&ctx, buffer, length); 49 | return [stream write:buffer maxLength:length]; 50 | } 51 | - (NSInteger)stream: (NSOutputStream *)stream writeData: (NSData *)data { 52 | return [self stream:stream write:(uint8_t *)[data bytes] maxLength:[data length]]; 53 | } 54 | - (NSInteger)writeChecksumToStream: (NSOutputStream *)stream { 55 | unsigned char checksum[CC_SHA1_DIGEST_LENGTH]; 56 | CC_SHA1_Final(checksum, &ctx); 57 | 58 | return [stream write:(uint8_t *)checksum maxLength:CC_SHA1_DIGEST_LENGTH]; 59 | } 60 | 61 | #pragma mark Object Addition Methods 62 | - (void)addObjectWithName: (GITObjectHash *)sha1 andData: (NSData *)data atOffset: (NSUInteger)offset { 63 | if ( offset > UINT32_MAX ) { 64 | return; // really need to put some error somewhere, or else convert 65 | } // us to a VersionTwo index file to save the user some trouble. 66 | 67 | [self addObjectHashToFanoutTable:sha1]; 68 | 69 | GITPackIndexWriterObject *obj = [GITPackIndexWriterObject indexWriterObjectWithName:sha1 atOffset:offset]; 70 | [objects addObject:obj]; 71 | (void)data; 72 | } 73 | - (void)addPackChecksum: (NSData *)packChecksumData { 74 | self.packChecksum = packChecksumData; 75 | } 76 | 77 | - (void)prepareForWriting { 78 | [self.objects sortUsingSelector:@selector(compare:)]; 79 | } 80 | 81 | #pragma mark Writer Methods 82 | - (NSInteger)writeObjectEntryToStream: (NSOutputStream *)stream { 83 | GITPackIndexWriterObject *obj = [objects objectAtIndex:objectsWritten++]; 84 | 85 | NSInteger written = 0; 86 | uint32_t offset = CFSwapInt32HostToBig((uint32_t)[obj offset]); 87 | written += [self stream:stream write:(uint8_t *)&offset maxLength:sizeof(offset)]; 88 | written += [self stream:stream writeData:[[obj sha1] packedData]]; 89 | return written; 90 | } 91 | - (NSInteger)writePackChecksumToStream: (NSOutputStream *)stream { 92 | return [self stream:stream writeData:packChecksum]; 93 | } 94 | - (NSInteger)writeToStream: (NSOutputStream *)stream { 95 | NSInteger written = 0; 96 | switch ( state ) { 97 | case 0: // write fanout table 98 | written = [self writeFanoutTableToStream:stream]; 99 | state = 1; 100 | break; 101 | case 1: // write object entries 102 | written = [self writeObjectEntryToStream:stream]; 103 | if ( objectsWritten >= [objects count] ) 104 | state = 2; 105 | break; 106 | case 2: // write pack checksum 107 | written = [self writePackChecksumToStream:stream]; 108 | state = 3; 109 | break; 110 | case 3: // write checksum 111 | written = [self writeChecksumToStream:stream]; 112 | state = 4; 113 | break; 114 | case 4: 115 | [stream close]; 116 | break; 117 | } 118 | return written; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /Vendor/OmniGroup/OmniBase/assertions.m: -------------------------------------------------------------------------------- 1 | // Copyright 1997-2006, 2008-2009 Omni Development, Inc. All rights reserved. 2 | // 3 | // This software may only be used and reproduced according to the 4 | // terms in the file OmniSourceLicense.html, which should be 5 | // distributed with this project and can also be found at 6 | // . 7 | 8 | #import "assertions.h" 9 | #import 10 | #import "OBUtilities.h" 11 | #import "OBBacktraceBuffer.h" 12 | #import // For getpid() 13 | 14 | #ifdef OMNI_ASSERTIONS_ON 15 | 16 | BOOL OBEnableExpensiveAssertions = NO; 17 | 18 | void OBLogAssertionFailure(const char *type, const char *expression, const char *file, unsigned int lineNumber) 19 | { 20 | fprintf(stderr, "%s failed: requires '%s', at %s:%d\n", type, expression, file, lineNumber); 21 | } 22 | 23 | static NSString *OBShouldAbortOnAssertFailureEnabled = @"OBShouldAbortOnAssertFailureEnabled"; 24 | static NSString *OBEnableExpensiveAssertionsKey = @"OBEnableExpensiveAssertions"; 25 | 26 | static void OBDefaultAssertionHandler(const char *type, const char *expression, const char *file, unsigned int lineNumber) 27 | { 28 | OBLogAssertionFailure(type, expression, file, lineNumber); 29 | 30 | if ([[NSUserDefaults standardUserDefaults] boolForKey:OBShouldAbortOnAssertFailureEnabled]) { 31 | // If we are running unit tests, abort on assertion failure. We could make assertions throw exceptions, but note that this wouldn't catch cases where you are using 'shouldRaise' and hit an assertion. 32 | #ifdef DEBUG 33 | // If we're failing in a debug build, give the developer a little time to connect in gdb before crashing 34 | fprintf(stderr, "You have 15 seconds to attach to pid %u in gdb...\n", getpid()); 35 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:15.0]]; 36 | #endif 37 | abort(); 38 | } 39 | } 40 | 41 | static OBAssertionFailureHandler currentAssertionHandler = OBDefaultAssertionHandler; 42 | void OBSetAssertionFailureHandler(OBAssertionFailureHandler handler) 43 | { 44 | if (handler) 45 | currentAssertionHandler = handler; 46 | else 47 | currentAssertionHandler = OBDefaultAssertionHandler; 48 | } 49 | 50 | void OBInvokeAssertionFailureHandler(const char *type, const char *expression, const char *file, unsigned int lineNumber) 51 | { 52 | OBRecordBacktrace(0, OBBacktraceBuffer_OBAssertionFailure); 53 | currentAssertionHandler(type, expression, file, lineNumber); 54 | OBAssertFailed(); 55 | } 56 | 57 | void OBAssertFailed(void) 58 | { 59 | // For breakpoints 60 | } 61 | 62 | void _OBAssertNotImplemented(id self, SEL sel) 63 | { 64 | if ([self respondsToSelector:sel]) { 65 | Class impClass = OBClassImplementingMethod([self class], sel); 66 | NSLog(@"%@ has implementation of %@", NSStringFromClass(impClass), NSStringFromSelector(sel)); 67 | OBAssertFailed(); 68 | } 69 | } 70 | 71 | #endif 72 | 73 | #if defined(OMNI_ASSERTIONS_ON) || defined(DEBUG) 74 | 75 | static void _OBAssertionLoad(void) __attribute__((constructor)); 76 | static void _OBAssertionLoad(void) 77 | { 78 | #ifdef OMNI_ASSERTIONS_ON 79 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 80 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 81 | NSDictionary *assertionDefaults = [NSDictionary dictionaryWithObjectsAndKeys: 82 | [NSNumber numberWithBool:OBIsRunningUnitTests()], OBShouldAbortOnAssertFailureEnabled, 83 | [NSNumber numberWithBool:NO], OBEnableExpensiveAssertionsKey, 84 | nil]; 85 | [defaults registerDefaults:assertionDefaults]; 86 | OBEnableExpensiveAssertions = [defaults boolForKey:OBEnableExpensiveAssertionsKey]; 87 | if (getenv("OBASSERT_NO_BANNER") == NULL) { 88 | fprintf(stderr, "*** Assertions are ON ***\n"); 89 | for(NSString *key in assertionDefaults) { 90 | fprintf(stderr, " %s = %s\n", 91 | [key cStringUsingEncoding:NSUTF8StringEncoding], 92 | [defaults boolForKey:key]? "YES" : "NO"); 93 | } 94 | } 95 | [pool release]; 96 | #elif DEBUG 97 | if (getenv("OBASSERT_NO_BANNER") == NULL) 98 | fprintf(stderr, "*** Assertions are OFF ***\n"); 99 | #endif 100 | } 101 | #endif 102 | --------------------------------------------------------------------------------