diff --git a/.gitignore b/.gitignore index b193eb3f5..7caa37186 100644 --- a/.gitignore +++ b/.gitignore @@ -2,16 +2,6 @@ *.swp *~.nib -build/* -.build/* -build-iPhoneSimulator/* -build-iPhoneOS/* -objective-git.bridgesupport -ObjectiveGitFramework/build/* -ObjectiveGit-iOS.framework/* -External/libgit2*.a -External/libgit2-ios - *.pbxuser *.perspective *.perspectivev3 @@ -19,13 +9,10 @@ External/libgit2-ios *.mode1v3 *.mode2v3 -project.xcworkspace */xcuserdata/ *.xccheckout +*.xcscmblueprint -ObjectiveGitTests/fixtures/Fixtures/* - -External/ios-openssl/include -External/ios-openssl/lib +External/libgit2-mac.a +Carthage/Build -External/libssh2-ios diff --git a/.gitmodules b/.gitmodules index dc91df79e..0d7955b21 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,18 +1,21 @@ [submodule "libgit2"] path = External/libgit2 url = https://github.com/libgit2/libgit2.git -[submodule "ObjectiveGitTests/specta"] - path = ObjectiveGitTests/specta - url = https://github.com/github/specta.git -[submodule "ObjectiveGitTests/expecta"] - path = ObjectiveGitTests/expecta - url = https://github.com/github/expecta.git -[submodule "Configuration"] - path = External/Configuration - url = https://github.com/jspahrsummers/xcconfigs.git [submodule "openssl"] path = External/openssl url = https://github.com/openssl/openssl.git [submodule "libssh2"] path = External/libssh2 - url = git://git.libssh2.org/libssh2.git + url = https://github.com/libssh2/libssh2.git +[submodule "Carthage/Checkouts/Nimble"] + path = Carthage/Checkouts/Nimble + url = https://github.com/Quick/Nimble.git +[submodule "Carthage/Checkouts/Quick"] + path = Carthage/Checkouts/Quick + url = https://github.com/Quick/Quick.git +[submodule "Carthage/Checkouts/xcconfigs"] + path = Carthage/Checkouts/xcconfigs + url = https://github.com/jspahrsummers/xcconfigs.git +[submodule "Carthage/Checkouts/ZipArchive"] + path = Carthage/Checkouts/ZipArchive + url = https://github.com/ZipArchive/ZipArchive.git diff --git a/.travis.yml b/.travis.yml index a6638609f..52c7d6dcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,28 @@ +# +# .travis.yml +# Objective-Git +# +# https://docs.travis-ci.com/user/reference/osx/ +# https://docs.travis-ci.com/user/build-stages/matrix-expansion/ +# +--- +os: osx +osx_image: xcode12.2 language: objective-c -install: -- brew install cmake + +matrix: + fast_finish: true + include: + - env: + - SCHEME="ObjectiveGit Mac" + - env: + - SCHEME="ObjectiveGit iOS" + +before_install: + - gem install xcpretty + - gem install xcpretty-travis-formatter +install: script/bootstrap script: script/cibuild + notifications: email: false - campfire: - on_success: always - on_failure: always - template: - - "Build %{build_url} of %{repository}: %{message}" - rooms: - secure: IxO/lskcwkcrvbgq9UsI4rjD7auSbbc52QdOcEzh5klXf6KkmIRSrJDUbsRGJ6GahlB0ELfgTcr0ycqsYhW5M9mNwzWR1cerbedA2HT0sOy7uvgGQwAY+iiikCsIMuYQJ1qvb6DrrpgyejQWXW5cOEVbNuoPV45DE0lsPUHcpyg= diff --git a/Cartfile b/Cartfile new file mode 100644 index 000000000..e69de29bb diff --git a/Cartfile.private b/Cartfile.private new file mode 100644 index 000000000..b021ce726 --- /dev/null +++ b/Cartfile.private @@ -0,0 +1,4 @@ +github "jspahrsummers/xcconfigs" "master" +github "Quick/Quick" ~> 2.1.0 +github "Quick/Nimble" ~> 8.0.1 +github "ZipArchive/ZipArchive" ~> 2.2.3 diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 000000000..1b6153da6 --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1,4 @@ +github "Quick/Nimble" "v8.1.2" +github "Quick/Quick" "v2.2.1" +github "ZipArchive/ZipArchive" "v2.2.3" +github "jspahrsummers/xcconfigs" "4ced0ad5a971220917994a4edfa6abf9702e3818" diff --git a/Carthage/Checkouts/Nimble b/Carthage/Checkouts/Nimble new file mode 160000 index 000000000..7a46a5fc8 --- /dev/null +++ b/Carthage/Checkouts/Nimble @@ -0,0 +1 @@ +Subproject commit 7a46a5fc86cb917f69e3daf79fcb045283d8f008 diff --git a/Carthage/Checkouts/Quick b/Carthage/Checkouts/Quick new file mode 160000 index 000000000..09b3becb3 --- /dev/null +++ b/Carthage/Checkouts/Quick @@ -0,0 +1 @@ +Subproject commit 09b3becb37cb2163919a3842a4c5fa6ec7130792 diff --git a/Carthage/Checkouts/ZipArchive b/Carthage/Checkouts/ZipArchive new file mode 160000 index 000000000..f3379e6ef --- /dev/null +++ b/Carthage/Checkouts/ZipArchive @@ -0,0 +1 @@ +Subproject commit f3379e6efa93e657293573ad196302b94041e464 diff --git a/Carthage/Checkouts/xcconfigs b/Carthage/Checkouts/xcconfigs new file mode 160000 index 000000000..4ced0ad5a --- /dev/null +++ b/Carthage/Checkouts/xcconfigs @@ -0,0 +1 @@ +Subproject commit 4ced0ad5a971220917994a4edfa6abf9702e3818 diff --git a/Classes/Categories/NSArray+StringArray.h b/Classes/Categories/NSArray+StringArray.h deleted file mode 100644 index 209511df5..000000000 --- a/Classes/Categories/NSArray+StringArray.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// NSArray+StringArray.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 08/08/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -#import "git2.h" - -@interface NSArray (StringArray) - -// Creates an `NSString` array from a `git_strarray`. -// -// The `git_strarray` must still be freed using `git_strarray_free` after use. -// -// strarray - The `git_strarray` to convert. -// -// Returns a new array with the contents of `strarray` converted to `NSString`s. -+ (instancetype)git_arrayWithStrarray:(git_strarray)strarray; - -// Creates and returns a `git_strarray` given an `NSArray` of `NSString`s. -// -// Must only be called with an array of `NSString`s, otherwise an assertion will -// fail. -// -// Returns a `git_strarray` which must be freed using `git_strarray_free` after -// use. -- (git_strarray)git_strarray; - -@end diff --git a/Classes/Categories/NSData+Git.h b/Classes/Categories/NSData+Git.h deleted file mode 100644 index 96371fdaa..000000000 --- a/Classes/Categories/NSData+Git.h +++ /dev/null @@ -1,12 +0,0 @@ -// -// NSData+Git.h -// - -#import "git2.h" - -@interface NSData (Git) - -+ (NSData *)git_dataWithOid:(git_oid *)oid; -- (BOOL)git_getOid:(git_oid *)oid error:(NSError **)error; - -@end diff --git a/Classes/Categories/NSData+Git.m b/Classes/Categories/NSData+Git.m deleted file mode 100644 index 715662b78..000000000 --- a/Classes/Categories/NSData+Git.m +++ /dev/null @@ -1,30 +0,0 @@ -// -// NSData+Git.m -// - -#import "NSData+Git.h" -#import "NSError+Git.h" - -@implementation NSData (Git) - -+ (NSData *)git_dataWithOid:(git_oid *)oid { - return [NSData dataWithBytes:oid length:sizeof(git_oid)]; -} - -- (BOOL)git_getOid:(git_oid *)oid error:(NSError **)error { - if ([self length] != sizeof(git_oid)) { - if (error != NULL) { - *error = [NSError errorWithDomain:GTGitErrorDomain - code:GITERR_INVALID - userInfo: - [NSDictionary dictionaryWithObject:@"can't extract oid from data of incorrect length" - forKey:NSLocalizedDescriptionKey]]; - } - return NO; - } - - [self getBytes:oid length:sizeof(git_oid)]; - return YES; -} - -@end diff --git a/Classes/Categories/NSDate+GTTimeAdditions.h b/Classes/Categories/NSDate+GTTimeAdditions.h deleted file mode 100644 index a7428d408..000000000 --- a/Classes/Categories/NSDate+GTTimeAdditions.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// NSDate+GTTimeAdditions.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 27/03/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -#import "git2.h" - -@interface NSDate (GTTimeAdditions) - -// Creates a new `NSDate` from the provided `git_time`. -// -// time - The `git_time` to base the returned date on. -// -// Returns an `NSDate` object representing the passed in `time`. -+ (NSDate *)gt_dateFromGitTime:(git_time)time; - -// Converts the date to a `git_time`. -// -// timeZone - An `NSTimeZone` to describe the time offset. This is optional, if -// `nil` the default time zone will be used. -- (git_time)gt_gitTimeUsingTimeZone:(NSTimeZone *)timeZone; - -@end - -@interface NSTimeZone (GTTimeAdditions) - -// Creates a new `NSTimeZone` from the provided `git_time`. -// -// Returns an `NSTimeZone` object representing the time zone for the offset in -// the passed in `git_time`. -+ (NSTimeZone *)gt_timeZoneFromGitTime:(git_time)time; - -// The difference, in minutes, between the current default timezone and GMT. -@property (nonatomic, readonly) int gt_gitTimeOffset; - -@end diff --git a/Classes/Categories/NSError+Git.h b/Classes/Categories/NSError+Git.h deleted file mode 100644 index 4e927135f..000000000 --- a/Classes/Categories/NSError+Git.h +++ /dev/null @@ -1,71 +0,0 @@ -// -// NSError+Git.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 2/17/11. -// -// The MIT License -// -// Copyright (c) 2011 Tim Clem -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -extern NSString * const GTGitErrorDomain; - -@interface NSError (Git) - -// Describes the given libgit2 error code, using any message provided by libgit2 -// or the OS. -// -// code - The error code returned from libgit2. -// -// Returns a non-nil NSError. -+ (NSError *)git_errorFor:(int)code; - -// Describes the given libgit2 error code, using `desc` as the error's -// description, and a failure reason from `reason` and the arguments that -// follow. -// -// The created error will also have an `NSUnderlyingErrorKey` that contains the -// result of +git_errorFor: on the same error code. -// -// code - The error code returned from libgit2. -// desc - The description to use in the created NSError. This may be nil. -// reason - A format string to use for the created NSError's failure reason. -// This may be nil. -// ... - Format arguments to insert into `reason`. -// -// Returns a non-nil NSError. -+ (NSError *)git_errorFor:(int)code description:(NSString *)desc failureReason:(NSString *)reason, ... NS_FORMAT_FUNCTION(3, 4); - -// Describes the given libgit2 error code, using `desc` and the arguments that -// follow as the error's description. -// -// The created error will also have an `NSUnderlyingErrorKey` that contains the -// result of +git_errorFor: on the same error code. -// -// code - The error code returned from libgit2. -// desc - A format string to use for the created NSError's description. This may be nil. -// ... - Format arguments to insert into `desc`. -// -// Returns a non-nil NSError. -+ (NSError *)git_errorFor:(int)code description:(NSString *)desc, ... NS_FORMAT_FUNCTION(2, 3); - -@end diff --git a/Classes/GTBlame.h b/Classes/GTBlame.h deleted file mode 100644 index c8740e5c9..000000000 --- a/Classes/GTBlame.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// GTBlame.h -// ObjectiveGitFramework -// -// Created by David Catmull on 11/6/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import "git2.h" - -@class GTBlameHunk; -@class GTRepository; - -// A `GTBlame` provides authorship info, through `GTBlameHunk` for each line of a file. -@interface GTBlame : NSObject - -// Designated initializer. -- (instancetype)initWithGitBlame:(git_blame *)blame; - -// Get all the hunks in the blame. A convenience wrapper around `enumerateHunksUsingBlock:` -@property (nonatomic, strong, readonly) NSArray *hunks; - -// The number of hunks in the blame. -@property (nonatomic, readonly) NSUInteger hunkCount; - -// Get the hunk at the specified index. -// -// index - The index to retrieve the hunk from. -// -// Returns a `GTBlameHunk` or nil if an error occurred. -- (GTBlameHunk *)hunkAtIndex:(NSUInteger)index; - -// Enumerate the hunks in the blame. -// -// block - A block invoked for every hunk in the blame. -// Setting stop to `YES` instantly stops the enumeration. -// -- (void)enumerateHunksUsingBlock:(void (^)(GTBlameHunk *hunk, NSUInteger index, BOOL *stop))block; - -// Get the hunk that relates to the given line number in the newest commit. -// -// lineNumber - The (1 based) line number to find a hunk for. -// -// Returns a `GTBlameHunk` or nil if an error occurred. -- (GTBlameHunk *)hunkAtLineNumber:(NSUInteger)lineNumber; - -// The underlying `git_blame` object. -- (git_blame *)git_blame __attribute__((objc_returns_inner_pointer)); - -@end - diff --git a/Classes/GTBlameHunk.h b/Classes/GTBlameHunk.h deleted file mode 100644 index 98c027a05..000000000 --- a/Classes/GTBlameHunk.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// GTBlameHunk.h -// ObjectiveGitFramework -// -// Created by David Catmull on 11/6/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import "git2.h" - -@class GTOID; -@class GTSignature; - -// A `GTBlameHunk` is an object that provides authorship info for a set of lines in a `GTBlame`. -@interface GTBlameHunk : NSObject - -- (instancetype)initWithGitBlameHunk:(git_blame_hunk)hunk; - -// A NSRange where `location` is the (1 based) starting line number, -// and `length` is the number of lines in the hunk. -@property (nonatomic, readonly) NSRange lines; - -// The OID of the commit where this hunk was last changed. -@property (nonatomic, readonly, copy) GTOID *finalCommitOID; - -// The signature of the commit where this hunk was last changed. -@property (nonatomic, readonly) GTSignature *finalSignature; - -// The path of the file in the original commit. -@property (nonatomic, readonly, copy) NSString *originalPath; - -// `YES` if the blame stopped trying before the commit where the line was added was found. -// This could happen if you use `GTBlameOptionsOldestCommitOID`. -@property (nonatomic, getter = isBoundary, readonly) BOOL boundary; - -// The git_blame_hunk represented by the receiver. -@property (nonatomic, readonly) git_blame_hunk git_blame_hunk; - -@end diff --git a/Classes/GTBlob.h b/Classes/GTBlob.h deleted file mode 100644 index 7e42beb44..000000000 --- a/Classes/GTBlob.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// GTBlob.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 2/25/11. -// -// The MIT License -// -// Copyright (c) 2011 Tim Clem -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - - -#import "GTObject.h" - - -@interface GTBlob : GTObject - -// Convenience class methods -+ (instancetype)blobWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error; -+ (instancetype)blobWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error; -+ (instancetype)blobWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error; - -// Convenience wrapper around `-initWithData:inRepository:error` that converts the string to UTF8 data -- (instancetype)initWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error; - -// Creates a new blob from the passed data. -// -// This writes data to the repository's object database. -// -// data - The data to write. -// repository - The repository to put the object in. -// error - Will be set if an error occurs. -// -// Returns a newly created blob object, or nil if an error occurs. -- (instancetype)initWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error; - -// Creates a new blob from the specified file. -// -// This copies the data from the file to the repository's object database. -// -// data - The file to copy contents from. -// repository - The repository to put the object in. -// error - Will be set if an error occurs. -// -// Returns a newly created blob object, or nil if an error occurs. -- (instancetype)initWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error; - -// The underlying `git_object` as a `git_blob` object. -- (git_blob *)git_blob __attribute__((objc_returns_inner_pointer)); - -- (git_off_t)size; -- (NSString *)content; -- (NSData *)data; - -@end diff --git a/Classes/GTBranch.h b/Classes/GTBranch.h deleted file mode 100644 index 60a656271..000000000 --- a/Classes/GTBranch.h +++ /dev/null @@ -1,102 +0,0 @@ -// -// GTBranch.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 3/3/11. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import "GTObject.h" - -@class GTCommit; -@class GTReference; -@class GTRepository; - -typedef enum { - GTBranchTypeLocal = GIT_BRANCH_LOCAL, - GTBranchTypeRemote = GIT_BRANCH_REMOTE, -} GTBranchType; - -/// A git branch object. -/// -/// Branches are considered to be equivalent iff both their `name` and `SHA` are -/// equal. -@interface GTBranch : NSObject - -@property (nonatomic, readonly) NSString *name; -@property (nonatomic, readonly) NSString *shortName; -@property (nonatomic, readonly) NSString *SHA; -@property (nonatomic, readonly) NSString *remoteName; -@property (nonatomic, readonly) GTBranchType branchType; -@property (nonatomic, readonly, strong) GTRepository *repository; -@property (nonatomic, readonly, strong) GTReference *reference; - -+ (NSString *)localNamePrefix; -+ (NSString *)remoteNamePrefix; - -- (id)initWithReference:(GTReference *)ref repository:(GTRepository *)repo; -+ (id)branchWithReference:(GTReference *)ref repository:(GTRepository *)repo; - -// Get the target commit for this branch -// -// error(out) - will be filled if an error occurs -// -// returns a GTCommit object or nil if an error occurred -- (GTCommit *)targetCommitAndReturnError:(NSError **)error; - -// Count all commits in this branch -// -// error(out) - will be filled if an error occurs -// -// returns number of commits in the branch or NSNotFound if an error occurred -- (NSUInteger)numberOfCommitsWithError:(NSError **)error; - -- (NSArray *)uniqueCommitsRelativeToBranch:(GTBranch *)otherBranch error:(NSError **)error; - -// Deletes the local branch and nils out the reference. -- (BOOL)deleteWithError:(NSError **)error; - -// If the receiver is a local branch, looks up and returns its tracking branch. -// If the receiver is a remote branch, returns self. If no tracking branch was -// found, returns nil and sets `success` to YES. -- (GTBranch *)trackingBranchWithError:(NSError **)error success:(BOOL *)success; - -// Reloads the branch's reference and creates a new branch based off that newly -// loaded reference. -// -// This does *not* change the receiver. -// -// error - The error if one occurred. -// -// Returns the reloaded branch, or nil if an error occurred. -- (GTBranch *)reloadedBranchWithError:(NSError **)error; - -// Calculate the ahead/behind count from this branch to the given branch. -// -// ahead - The number of commits which are unique to the receiver. Cannot be -// NULL. -// behind - The number of commits which are unique to `branch`. Cannot be NULL. -// branch - The branch to which the receiver should be compared. -// error - The error if one occurs. -// -// Returns whether the calculation was successful. -- (BOOL)calculateAhead:(size_t *)ahead behind:(size_t *)behind relativeTo:(GTBranch *)branch error:(NSError **)error; - -@end diff --git a/Classes/GTConfiguration+Private.h b/Classes/GTConfiguration+Private.h deleted file mode 100644 index 963430378..000000000 --- a/Classes/GTConfiguration+Private.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// GTConfiguration+Private.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 9/12/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "GTConfiguration.h" - -@class GTRepository; - -@interface GTConfiguration () - -// Initializes the receiver. -// -// config - The libgit2 config. Cannot be NULL. -// repository - The repository in which the config resides. May be nil. -// -// Returns the initialized object. -- (id)initWithGitConfig:(git_config *)config repository:(GTRepository *)repository; - -@end diff --git a/Classes/GTConfiguration.h b/Classes/GTConfiguration.h deleted file mode 100644 index 97877d5b9..000000000 --- a/Classes/GTConfiguration.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// GTConfiguration.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 12/30/11. -// Copyright (c) 2011 GitHub, Inc. All rights reserved. -// - -#include "git2.h" - -@class GTRepository; -@class GTSignature; - -@interface GTConfiguration : NSObject - -@property (nonatomic, readonly, strong) GTRepository *repository; -@property (nonatomic, readonly, copy) NSArray *configurationKeys; - -// The GTRemotes in the config. If the configuration isn't associated with any -// repository, this will always be nil. -@property (nonatomic, readonly, copy) NSArray *remotes; - -// Creates and returns a configuration which includes the global, XDG, and -// system configurations. -+ (instancetype)defaultConfiguration; - -// The underlying `git_config` object. -- (git_config *)git_config __attribute__((objc_returns_inner_pointer)); - -- (void)setString:(NSString *)s forKey:(NSString *)key; -- (NSString *)stringForKey:(NSString *)key; - -- (void)setBool:(BOOL)b forKey:(NSString *)key; -- (BOOL)boolForKey:(NSString *)key; - -- (void)setInt32:(int32_t)i forKey:(NSString *)key; -- (int32_t)int32ForKey:(NSString *)key; - -- (void)setInt64:(int64_t)i forKey:(NSString *)key; -- (int64_t)int64ForKey:(NSString *)key; - -- (BOOL)deleteValueForKey:(NSString *)key error:(NSError **)error; - -// Reloads the configuration from the files on disk if they have changed since -// it was originally loaded. -// -// error - The error if one occurred. -// -// Returns whether the refresh was successful. -- (BOOL)refresh:(NSError **)error; - -@end diff --git a/Classes/GTCredential.h b/Classes/GTCredential.h deleted file mode 100644 index ea4b2788f..000000000 --- a/Classes/GTCredential.h +++ /dev/null @@ -1,76 +0,0 @@ -// -// GTCredential.h -// ObjectiveGitFramework -// -// Created by Etienne on 10/09/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -// An enum describing the data needed for authentication. -// See `git_credtype_t`. -typedef enum { - GTCredentialTypeUserPassPlaintext = GIT_CREDTYPE_USERPASS_PLAINTEXT, - GTCredentialTypeSSHKey = GIT_CREDTYPE_SSH_KEY, - GTCredentialTypeSSHCustom = GIT_CREDTYPE_SSH_CUSTOM, -} GTCredentialType; - -@class GTCredential; - -// The GTCredentialProvider acts as a proxy for GTCredential requests. -// -// The default implementation is used through `+providerWithBlock:`, -// passing your own block that will build a GTCredential object. -// But you're allowed to subclass it and handle more complex workflows. -@interface GTCredentialProvider : NSObject - -// Creates a provider from a block. -// -// credentialBlock - a block that will be called when credentials are requested. -+ (instancetype)providerWithBlock:(GTCredential *(^)(GTCredentialType type, NSString *URL, NSString *userName))credentialBlock; - -// Default credential provider method. -// -// This method will get called when an operation requests credentials from the -// provider. -// -// The default implementation calls through the `providedBlock` passed -// in `providerWithBlock:` above, but your subclass is expected to override it -// to do its specific work. -// -// type - the credential types allowed by the operation. -// URL - the URL the operation is authenticating against. -// userName - the user name provided by the operation. Can be nil, and might be ignored. -- (GTCredential *)credentialForType:(GTCredentialType)type URL:(NSString *)URL userName:(NSString *)userName; -@end - -// The GTCredential class is used to provide authentication data. -// It acts as a wrapper around `git_cred` objects. -@interface GTCredential : NSObject - -// Create a credential object from a username/password pair. -// -// userName - The username to authenticate as. -// password - The password belonging to that user. -// error - If not NULL, set to any errors that occur. -// -// Return a new GTCredential instance, or nil if an error occurred -+ (instancetype)credentialWithUserName:(NSString *)userName password:(NSString *)password error:(NSError **)error; - -// Create a credential object from a SSH keyfile -// -// userName - The username to authenticate as. -// publicKeyURL - The URL to the public key for that user. -// Can be omitted to reconstruct the public key from the private key. -// privateKeyURL - The URL to the private key for that user. -// passphrase - The passPhrase for the private key. Optional if the private key has no password. -// error - If not NULL, set to any errors that occur. -// -// Return a new GTCredential instance, or nil if an error occurred -+ (instancetype)credentialWithUserName:(NSString *)userName publicKeyURL:(NSURL *)publicKeyURL privateKeyURL:(NSURL *)privateKeyURL passphrase:(NSString *)passphrase error:(NSError **)error; - -// The underlying `git_cred` object. -- (git_cred *)git_cred __attribute__((objc_returns_inner_pointer)); - -@end diff --git a/Classes/GTDiff.h b/Classes/GTDiff.h deleted file mode 100644 index c975684eb..000000000 --- a/Classes/GTDiff.h +++ /dev/null @@ -1,301 +0,0 @@ -// -// GTDiff.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 29/11/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -#import "GTDiffDelta.h" - -@class GTDiffDelta; -@class GTRepository; -@class GTTree; - -// An `NSNumber` wrapped `GTDiffOptionsFlags` representing any flags you wish to -// pass into the initialisation. -extern NSString *const GTDiffOptionsFlagsKey; - -// An `NSNumber` wrapped `NSUInteger` dictating the number of unchanged lines -// that define the boundary of a hunk (and to display around it). -// -// Defaults to 3. -extern NSString *const GTDiffOptionsContextLinesKey; - -// An `NSNumber` wrapped `NSUInteger` dictating the maximum number of unchanged -// lines between hunk boundaries before the hunks will be merged. -// -// Defaults to 0. -extern NSString *const GTDiffOptionsInterHunkLinesKey; - -// An `NSString` to prefix old file names with. -// -// Defaults to "a". -extern NSString *const GTDiffOptionsOldPrefixKey; - -// An `NSString` to prefix new file names with. -// -// Defaults to "b". -extern NSString *const GTDiffOptionsNewPrefixKey; - -// An `NSNumber` wrapped `NSUInteger` determining the maximum size (in bytes) -// of a file to diff. Above this size the file will be treated as binary. -// -// Defaults to 512MB. -extern NSString *const GTDiffOptionsMaxSizeKey; - -// An `NSArray` of `NSStrings`s to limit the diff to specific paths inside the -// repository. The entries in the array represent either single paths or -// filename patterns with wildcard matching a la standard shell glob (see -// http://linux.die.net/man/7/glob for wildcard matching rules). -// -// The diff will only contain the files or patterns included in this options -// array. -// -// Defaults to including all files. -extern NSString *const GTDiffOptionsPathSpecArrayKey; - -// Enum for use as documented in the options dictionary with the -// `GTDiffOptionsFlagsKey` key. -// -// See diff.h for documentation of each individual flag. -typedef enum { - GTDiffOptionsFlagsNormal = GIT_DIFF_NORMAL, - - /* - * Options controlling which files will be in the diff - */ - - GTDiffOptionsFlagsReverse = GIT_DIFF_REVERSE, - GTDiffOptionsFlagsIncludeIgnored = GIT_DIFF_INCLUDE_IGNORED, - GTDiffOptionsFlagsRecurseIgnoredDirs = GIT_DIFF_RECURSE_IGNORED_DIRS, - GTDiffOptionsFlagsIncludeUntracked = GIT_DIFF_INCLUDE_UNTRACKED, - GTDiffOptionsFlagsRecurseUntrackedDirs = GIT_DIFF_RECURSE_UNTRACKED_DIRS, - GTDiffOptionsFlagsIncludeUnmodified = GIT_DIFF_INCLUDE_UNMODIFIED, - GTDiffOptionsFlagsIncludeTypeChange = GIT_DIFF_INCLUDE_TYPECHANGE, - GTDiffOptionsFlagsIncludeTypeChangeTrees = GIT_DIFF_INCLUDE_TYPECHANGE_TREES, - GTDiffOptionsFlagsIgnoreFileMode = GIT_DIFF_IGNORE_FILEMODE, - GTDiffOptionsFlagsIgnoreSubmodules = GIT_DIFF_IGNORE_SUBMODULES, - GTDiffOptionsFlagsIgnoreCase = GIT_DIFF_IGNORE_CASE, - GTDiffOptionsFlagsDisablePathspecMatch = GIT_DIFF_DISABLE_PATHSPEC_MATCH, - GTDiffOptionsFlagsSkipBinaryCheck = GIT_DIFF_SKIP_BINARY_CHECK, - GTDiffOptionsFlagsEnableFastUntrackedDirs = GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS, - - /* - * Options controlling how output will be generated - */ - - GTDiffOptionsFlagsForceText = GIT_DIFF_FORCE_TEXT, - GTDiffOptionsFlagsForceBinary = GIT_DIFF_FORCE_BINARY, - GTDiffOptionsFlagsIgnoreWhitespace = GIT_DIFF_IGNORE_WHITESPACE, - GTDiffOptionsFlagsIgnoreWhitespaceChange = GIT_DIFF_IGNORE_WHITESPACE_CHANGE, - GTDiffOptionsFlagsIgnoreWhitespaceEOL = GIT_DIFF_IGNORE_WHITESPACE_EOL, - GTDiffOptionsFlagsShowUntrackedContent = GIT_DIFF_SHOW_UNTRACKED_CONTENT, - GTDiffOptionsFlagsShowUnmodified = GIT_DIFF_SHOW_UNMODIFIED, - - GTDiffOptionsFlagsPatience = GIT_DIFF_PATIENCE, - GTDiffOptionsFlagsMinimal = GIT_DIFF_MINIMAL, -} GTDiffOptionsFlags; - -// An `NSNumber` wrapped `GTDiffOptionsFlags` bitmask containing any of the -// flags documented below. -// -// Defualts to `GTDiffFindOptionsFlagsFindRenames`. -extern NSString *const GTDiffFindOptionsFlagsKey; - -// An `NSNumber` wrapped `NSUInteger` dictating the similarity between files -// to be considered a rename. -// -// This is a value as per the git similarity index and should be between 1 and -// 100 (0 and above 100 use the default). -// -// Defaults to 50. -extern NSString *const GTDiffFindOptionsRenameThresholdKey; - -// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can -// be to be eligable as a rename. -// -// This is a value as per the git similarity index and should be between 1 and -// 100 (0 and above 100 use the default). -// -// Defaults to 50. -extern NSString *const GTDiffFindOptionsRenameFromRewriteThresholdKey; - -// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can -// be to be considered a copy. -// -// This is a value as per the git similarity index and should be between 1 and -// 100 (0 and above 100 use the default). -// -// Defaults to 50. -extern NSString *const GTDiffFindOptionsCopyThresholdKey; - -// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can -// be to be to be broken into a separate deletion and addition pair. -// -// This is a value as per the git similarity index and should be between 1 and -// 100 (0 and above 100 use the default). -// -// Defaults to 60. -extern NSString *const GTDiffFindOptionsBreakRewriteThresholdKey; - -// An `NSNumber` wrapped `NSUInteger` dictating the maximum amount of similarity -// sources to examine. -// -// This is the equivalent of the `diff.renameLimit` config value. -// -// Defaults to 200. -extern NSString *const GTDiffFindOptionsRenameLimitKey; - -// Enum for options passed into `-findSimilarWithOptions:`. -// -// For individual case documentation see `diff.h`. -typedef enum { - GTDiffFindOptionsFlagsFindRenames = GIT_DIFF_FIND_RENAMES, - GTDiffFindOptionsFlagsFindRenamesFromRewrites = GIT_DIFF_FIND_RENAMES_FROM_REWRITES, - GTDiffFindOptionsFlagsFindCopies = GIT_DIFF_FIND_COPIES, - GTDiffFindOptionsFlagsFindCopiesFromUnmodified = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED, - GTDiffFindOptionsFlagsFindRewrites = GIT_DIFF_FIND_REWRITES, - GTDiffFindOptionsFlagsBreakRewrites = GIT_DIFF_BREAK_REWRITES, - GTDiffFindOptionsFlagsFindAndBreakRewrites = GIT_DIFF_FIND_AND_BREAK_REWRITES, - - GTDiffFindOptionsFlagsFindForUntracked = GIT_DIFF_FIND_FOR_UNTRACKED, - GTDiffFindAll = GIT_DIFF_FIND_ALL, - - GTDiffFindOptionsFlagsIgnoreLeadingWhitespace = GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE, - GTDiffFindOptionsFlagsIgnoreWhitespace = GIT_DIFF_FIND_IGNORE_WHITESPACE, - GTDiffFindOptionsFlagsDontIgnoreWhitespace = GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE, - GTDiffFindOptionsFlagsExactMatchOnly = GIT_DIFF_FIND_EXACT_MATCH_ONLY, - - GTDiffFindOptionsFlagsBreakRewritesForRenamesOnly = GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY, -} GTDiffFindOptionsFlags; - -// A class representing a single "diff". -// -// Analagous to `git_diff_list` in libgit2, this object represents a list of -// changes or "deltas", which are represented by `GTDiffDelta` objects. -@interface GTDiff : NSObject - -// The number of deltas represented by the diff object. -@property (nonatomic, readonly) NSUInteger deltaCount; - -// Create a diff between 2 `GTTree`s. -// -// The 2 trees must be from the same repository, or an exception will be thrown. -// -// oldTree - The "left" side of the diff. May be nil to represent an empty -// tree. -// newTree - The "right" side of the diff. May be nil to represent an empty -// tree. -// repository - The repository to be used for the diff. Cannot be nil. -// options - A dictionary containing any of the above options key constants, or -// nil to use the defaults. -// error - Populated with an `NSError` object on error, if information is -// available. -// -// Returns a newly created `GTDiff` object or nil on error. -+ (GTDiff *)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error; - -// Create a diff between a repository's current index. -// -// This is equivalent to `git diff --cached ` or if you pass the HEAD -// tree, then `git diff --cached`. -// -// The tree you pass will be used for the "left" side of the diff, and the -// index will be used for the "right" side of the diff. -// -// tree - The tree to be diffed. The index will be taken from this tree's -// repository. The left side of the diff. May be nil to represent an -// empty tree. -// repository - The repository to be used for the diff. -// options - A dictionary containing any of the above options key constants, or -// nil to use the defaults. -// error - Populated with an `NSError` object on error, if information is -// available. -// -// Returns a newly created `GTDiff` object or nil on error. -+ (GTDiff *)diffIndexFromTree:(GTTree *)tree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error; - -// Create a diff between the index and working directory in a given repository. -// -// This matches the `git diff` command. -// -// repository - The repository to be used for the diff. -// options - A dictionary containing any of the above options key constants, -// or nil to use the defaults. -// error - Populated with an `NSError` object on error, if information is -// available. -// -// Returns a newly created `GTDiff` object or nil on error. -+ (GTDiff *)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error; - -// Create a diff between a repository's working directory and a tree. -// -// tree - The tree to be diffed. The tree will be the left side of the diff. -// May be nil to represent an empty tree. -// repository - The repository to be used for the diff. -// options - A dictionary containing any of the above options key constants, or -// nil to use the defaults. -// error - Populated with an `NSError` object on error, if information is -// available. -// -// Returns a newly created `GTDiff` object or nil on error. -+ (GTDiff *)diffWorkingDirectoryFromTree:(GTTree *)tree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error; - -// Create a diff between the working directory and HEAD. -// -// If the repository does not have a HEAD commit yet, this will create a diff of -// the working directory as if everything would be part of the initial commit. -// -// repository - The repository to be used for the diff. -// options - A dictionary containing any of the above options key constants, -// or nil to use the defaults. -// error - Populated if an error occurs. -// -// Returns a newly created GTDiff, or nil if an error occurred. -+ (GTDiff *)diffWorkingDirectoryToHEADInRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error; - -// Designated initialiser. -// -// diff - The diff to represent. Cannot be NULL. -// repository - The repository in which the diff lives. Cannot be nil. -// -// Returns the initialized object. -- (instancetype)initWithGitDiff:(git_diff *)diff repository:(GTRepository *)repository; - -// The libgit2 diff object. -- (git_diff *)git_diff __attribute__((objc_returns_inner_pointer)); - -// The number of deltas of the given type that are contained in the diff. -- (NSUInteger)numberOfDeltasWithType:(GTDiffDeltaType)deltaType; - -// Enumerate the deltas in a diff. -// -// It is worth noting that the `git_diff_patch` objects backing each delta -// contain the entire contents in memory. It is therefore recommended you -// do not store the `delta` object given here, but instead perform any work -// necessary within the provided block. -// -// Also note that this method blocks during the enumeration. -// -// block - A block to be executed for each delta. Setting `stop` to `YES` -// immediately stops the enumeration. -- (void)enumerateDeltasUsingBlock:(void (^)(GTDiffDelta *delta, BOOL *stop))block; - -// Modify the diff list to combine similar changes using the given options. -// -// options - A dictionary containing any of the above find options key constants -// or nil to use the defaults. -- (void)findSimilarWithOptions:(NSDictionary *)options; - -// Merge a diff with another diff. -// -// diff - the diff to merge in. -// error - Populated if an error occurs -// -// Returns YES if the merge was successfull, and NO and sets `error` otherwise. -- (BOOL)mergeDiffWithDiff:(GTDiff *)diff error:(NSError **)error; - -@end diff --git a/Classes/GTDiffDelta.h b/Classes/GTDiffDelta.h deleted file mode 100644 index 1153edab9..000000000 --- a/Classes/GTDiffDelta.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// GTDiffDelta.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 30/11/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "git2.h" -#import "GTDiffFile.h" - -@class GTDiff; -@class GTDiffHunk; -@class GTDiffPatch; - -// The type of change that this delta represents. -// -// GTDiffFileDeltaUnmodified - No Change. -// GTDiffFileDeltaAdded - The file was added to the index. -// GTDiffFileDeltaDeleted - The file was removed from the working directory. -// GTDiffFileDeltaModified - The file was modified. -// GTDiffFileDeltaRenamed - The file has been renamed. -// GTDiffFileDeltaCopied - The file was duplicated. -// GTDiffFileDeltaIgnored - The file was ignored by git. -// GTDiffFileDeltaUntracked - The file has been added to the working directory -// and is therefore currently untracked. -// GTDiffFileDeltaTypeChange - The file has changed from a blob to either a -// submodule, symlink or directory. Or vice versa. -typedef enum { - GTDiffFileDeltaUnmodified = GIT_DELTA_UNMODIFIED, - GTDiffFileDeltaAdded = GIT_DELTA_ADDED, - GTDiffFileDeltaDeleted = GIT_DELTA_DELETED, - GTDiffFileDeltaModified = GIT_DELTA_MODIFIED, - GTDiffFileDeltaRenamed = GIT_DELTA_RENAMED, - GTDiffFileDeltaCopied = GIT_DELTA_COPIED, - GTDiffFileDeltaIgnored = GIT_DELTA_IGNORED, - GTDiffFileDeltaUntracked = GIT_DELTA_UNTRACKED, - GTDiffFileDeltaTypeChange = GIT_DELTA_TYPECHANGE, -} GTDiffDeltaType; - -// A class representing a single change within a diff. -// -// The change may not be simply a change of text within a given file, it could -// be that the file was renamed, or added to the index. See `GTDiffDeltaType` -// for the types of change represented. -@interface GTDiffDelta : NSObject - -// The `git_diff_delta` represented by the receiver. -@property (nonatomic, assign, readonly) git_diff_delta git_diff_delta; - -// The diff in which this delta is contained. -@property (nonatomic, strong, readonly) GTDiff *diff; - -// Any flags set on the delta. See `GTDiffFileFlag` for more info. -// -// Note that this may not include `GTDiffFileFlagBinary` _or_ -// `GTDiffFileFlagNotBinary` until the content is loaded for this delta (e.g., -// through a call to -generatePatch:). -@property (nonatomic, assign, readonly) GTDiffFileFlag flags; - -// The file to the "left" of the diff. -@property (nonatomic, readonly, copy) GTDiffFile *oldFile; - -// The file to the "right" of the diff. -@property (nonatomic, readonly, copy) GTDiffFile *newFile __attribute__((ns_returns_not_retained)); - -// The type of change that this delta represents. -// -// Think "status" as in `git status`. -@property (nonatomic, readonly) GTDiffDeltaType type; - -// Initializes the receiver to wrap the delta at the given index. -- (instancetype)initWithDiff:(GTDiff *)diff deltaIndex:(NSUInteger)deltaIndex; - -// Creates a patch from a text delta. -// -// If the receiver represents a binary delta, this method will return an error. -// -// error - If not NULL, set to any error that occurs. -// -// Returns a new patch, or nil if an error occurs. -- (GTDiffPatch *)generatePatch:(NSError **)error; - -@end diff --git a/Classes/GTDiffDelta.m b/Classes/GTDiffDelta.m deleted file mode 100644 index 1776af458..000000000 --- a/Classes/GTDiffDelta.m +++ /dev/null @@ -1,78 +0,0 @@ -// -// GTDiffDelta.m -// ObjectiveGitFramework -// -// Created by Danny Greg on 30/11/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "GTDiffDelta.h" - -#import "GTDiff.h" -#import "GTDiffFile.h" -#import "GTDiffPatch.h" -#import "NSError+Git.h" - -@interface GTDiffDelta () - -// The index of this delta within its parent `diff`. -@property (nonatomic, assign, readonly) NSUInteger deltaIndex; - -@end - -@implementation GTDiffDelta - -#pragma mark Properties - -- (git_diff_delta)git_diff_delta { - return *(git_diff_get_delta(self.diff.git_diff, self.deltaIndex)); -} - -- (GTDiffFileFlag)flags { - return (GTDiffFileFlag)self.git_diff_delta.flags; -} - -- (GTDiffFile *)oldFile { - return [[GTDiffFile alloc] initWithGitDiffFile:self.git_diff_delta.old_file]; -} - -- (GTDiffFile *)newFile { - return [[GTDiffFile alloc] initWithGitDiffFile:self.git_diff_delta.new_file]; -} - -- (GTDiffDeltaType)type { - return (GTDiffDeltaType)self.git_diff_delta.status; -} - -#pragma mark Lifecycle - -- (instancetype)initWithDiff:(GTDiff *)diff deltaIndex:(NSUInteger)deltaIndex { - self = [super init]; - if (self == nil) return nil; - - _diff = diff; - _deltaIndex = deltaIndex; - - return self; -} - -#pragma mark Patch Generation - -- (GTDiffPatch *)generatePatch:(NSError **)error { - git_patch *patch = NULL; - int gitError = git_patch_from_diff(&patch, self.diff.git_diff, self.deltaIndex); - if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Patch generation failed for delta %@", self]; - return nil; - } - - return [[GTDiffPatch alloc] initWithGitPatch:patch delta:self]; -} - -#pragma mark NSObject - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p>{ flags: %u, oldFile: %@, newFile: %@ }", self.class, self, (unsigned)self.git_diff_delta.flags, self.oldFile, self.newFile]; -} - -@end diff --git a/Classes/GTDiffFile.h b/Classes/GTDiffFile.h deleted file mode 100644 index 8a2a1f485..000000000 --- a/Classes/GTDiffFile.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// GTDiffFile.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 30/11/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -// Flags which may be set on the file. -// -// GTDiffFileFlagBinaryMask - A mask to just retrieve the binary/not binary -// information from a set of flags. -// -// See diff.h for further documentation. -typedef enum { - GTDiffFileFlagValidID = GIT_DIFF_FLAG_VALID_ID, - GTDiffFileFlagBinary = GIT_DIFF_FLAG_BINARY, - GTDiffFileFlagNotBinary = GIT_DIFF_FLAG_NOT_BINARY, - - GTDiffFileFlagBinaryMask = GTDiffFileFlagBinary | GTDiffFileFlagNotBinary, -} GTDiffFileFlag; - -@class GTOID; - -// A class representing a file on one side of a diff. -@interface GTDiffFile : NSObject - -// The location within the working directory of the file. -@property (nonatomic, readonly, copy) NSString *path; - -// The size (in bytes) of the file. -@property (nonatomic, readonly) NSUInteger size; - -// Any flags set on the file (see `GTDiffFileFlag` for more info). -@property (nonatomic, readonly) GTDiffFileFlag flags; - -// The mode of the file. -@property (nonatomic, readonly) mode_t mode; - -// The OID for the file. -@property (nonatomic, readonly, copy) GTOID *OID; - -// The git_diff_file represented by the receiver. -@property (nonatomic, readonly) git_diff_file git_diff_file; - -/// Initializes the receiver with the provided libgit2 object. -/// -/// file - The git_diff_file wrapped by the receiver. -/// -/// Returns an initialized GTDiffFile. -- (instancetype)initWithGitDiffFile:(git_diff_file)file; - -@end diff --git a/Classes/GTDiffHunk.h b/Classes/GTDiffHunk.h deleted file mode 100644 index ae6e02c65..000000000 --- a/Classes/GTDiffHunk.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// GTDiffHunk.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 30/11/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -@class GTDiffLine; -@class GTDiffPatch; - -// A class representing a hunk within a diff patch. -@interface GTDiffHunk : NSObject - -// The header of the hunk. -@property (nonatomic, readonly, copy) NSString *header; - -// The number of lines represented in the hunk. -@property (nonatomic, readonly) NSUInteger lineCount; - -// Designated initialiser. -// -// The contents of a hunk are lazily loaded, therefore we initialise the object -// simply with the patch it originates from and which hunk index it represents. -- (instancetype)initWithPatch:(GTDiffPatch *)patch hunkIndex:(NSUInteger)hunkIndex; - -// Perfoms the given block on each line in the hunk. -// -// Note that this method blocks during the enumeration. -// -// error - A pointer to an NSError that will be set if one occurs. -// block - A block to execute on each line. Setting `stop` to `NO` will -// immediately stop the enumeration and return from the method. -// Return YES if the enumeration was successful, NO otherwise (and an error will -// be set in `error`). -- (BOOL)enumerateLinesInHunk:(NSError **)error usingBlock:(void (^)(GTDiffLine *line, BOOL *stop))block; - -@end diff --git a/Classes/GTDiffLine.h b/Classes/GTDiffLine.h deleted file mode 100644 index 08515230a..000000000 --- a/Classes/GTDiffLine.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// GTDiffLine.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 20/12/2012. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -// A character representing the origin of a given line. -// -// See diff.h for individual documentation. -typedef enum { - GTDiffLineOriginContext = GIT_DIFF_LINE_CONTEXT, - GTDiffLineOriginAddition = GIT_DIFF_LINE_ADDITION, - GTDiffLineOriginDeletion = GIT_DIFF_LINE_DELETION, - GTDiffLineOriginNoEOFNewlineContext = GIT_DIFF_LINE_CONTEXT_EOFNL, - GTDiffLineOriginAddEOFNewLine = GIT_DIFF_LINE_ADD_EOFNL, - GTDiffLineOriginDeleteEOFNewLine = GIT_DIFF_LINE_DEL_EOFNL, -} GTDiffLineOrigin; - -// Represents an individual line in a diff hunk. -@interface GTDiffLine : NSObject - -// The content string of the line. -@property (nonatomic, readonly, copy) NSString *content; - -// The line number of this line in the left side of the diff. -// -// -1 if the line is an addition. -@property (nonatomic, readonly) NSInteger oldLineNumber; - -// The line number of this line in the right side of the diff. -// -// -1 if the line is a deletion. -@property (nonatomic, readonly) NSInteger newLineNumber; - -// The origin of the line, see the enum above for possible values. -@property (nonatomic, readonly) GTDiffLineOrigin origin; - -// The number of newlines appearing in `-content`. -@property (nonatomic, readonly) NSInteger lineCount; - -// Designated initialiser. -- (instancetype)initWithGitLine:(const git_diff_line *)line; - -@end diff --git a/Classes/GTDiffPatch.h b/Classes/GTDiffPatch.h deleted file mode 100644 index befaa916a..000000000 --- a/Classes/GTDiffPatch.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// GTDiffPatch.h -// ObjectiveGitFramework -// -// Created by Justin Spahr-Summers on 2014-02-27. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -@class GTDiffHunk; -@class GTDiffDelta; - -// Represents one or more text changes to a single file within a diff. -@interface GTDiffPatch : NSObject - -// The delta corresponding to this patch. -@property (nonatomic, strong, readonly) GTDiffDelta *delta; - -// The number of added lines in this patch. -@property (nonatomic, assign, readonly) NSUInteger addedLinesCount; - -// The number of deleted lines in this patch. -@property (nonatomic, assign, readonly) NSUInteger deletedLinesCount; - -// The number of context lines in this patch. -@property (nonatomic, assign, readonly) NSUInteger contextLinesCount; - -// The number of hunks in this patch. -@property (nonatomic, readonly) NSUInteger hunkCount; - -// Initializes the receiver to wrap the given patch. -// -// patch - The patch object to wrap and take ownership of. This will -// automatically be freed when the receiver is deallocated. Must not be -// NULL. -// delta - The diff delta corresponding to this patch. Must not be nil. -- (instancetype)initWithGitPatch:(git_patch *)patch delta:(GTDiffDelta *)delta; - -// Returns the underlying patch object. -- (git_patch *)git_patch __attribute__((objc_returns_inner_pointer)); - -// Get the size of this patch. -// -// includeContext - Whether to include the context lines in the size. -// includeHunkHeaders - Whether to include the hunk header lines in the size. -// includeFileHeaders - Whether to include the file header lines in the size. -// -// Returns the raw size of the delta, in bytes. -- (NSUInteger)sizeWithContext:(BOOL)includeContext hunkHeaders:(BOOL)includeHunkHeaders fileHeaders:(BOOL)includeFileHeaders; - -// Enumerate the hunks contained in the patch. -// -// This enumeration is synchronous, and will block the calling thread while -// generating hunk content. -// -// block - A block to be executed for each hunk. Setting `stop` to `YES` -// will stop the enumeration after the block returns. -// -// Returns whether enumeration was successful, or terminated early. If `NO`, an -// error occurred during enumeration. -- (BOOL)enumerateHunksUsingBlock:(void (^)(GTDiffHunk *hunk, BOOL *stop))block; - -@end diff --git a/Classes/GTEnumerator.h b/Classes/GTEnumerator.h deleted file mode 100644 index 352174fe9..000000000 --- a/Classes/GTEnumerator.h +++ /dev/null @@ -1,132 +0,0 @@ -// -// GTEnumerator.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 2/21/11. -// -// The MIT License -// -// Copyright (c) 2011 Tim Clem -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import "GTObject.h" - -// Options to specify enumeration order when enumerating through a repository. -// With the exception of GTEnumeratorOptionsNone, the values here can be ORed -// together to combine their behaviors. -// -// GTEnumeratorOptionsNone - Implementation-defined sorting. -// GTEnumeratorOptionsTopologicalSort - Sort parents before children. -// GTEnumeratorOptionsTimeSort - Sort by commit time. -// GTEnumeratorOptionsReverse - Iterate in reverse order. -typedef enum : unsigned int { - GTEnumeratorOptionsNone = GIT_SORT_NONE, - GTEnumeratorOptionsTopologicalSort = GIT_SORT_TOPOLOGICAL, - GTEnumeratorOptionsTimeSort = GIT_SORT_TIME, - GTEnumeratorOptionsReverse = GIT_SORT_REVERSE, -} GTEnumeratorOptions; - -@class GTRepository; -@class GTCommit; - -// Enumerates the commits in a repository. -@interface GTEnumerator : NSEnumerator - -// The repository being enumerated. -@property (nonatomic, strong, readonly) GTRepository *repository; - -// The options currently being used for enumeration. -// -// To set new options, use -resetWithOptions:. -@property (nonatomic, assign, readonly) GTEnumeratorOptions options; - -// Initializes the receiver to enumerate the commits in the given repository. -// -// repo - The repository to enumerate the commits of. This must not be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns an initialized enumerator, or nil if an error occurs. -- (id)initWithRepository:(GTRepository *)repo error:(NSError **)error; - -// Marks a commit to start traversal from. -// -// sha - The SHA of a commit in the receiver's repository. This must not be -// nil. -// error - If not NULL, this will be set to any error that occurs. -// -// Returns whether pushing the commit was successful. -- (BOOL)pushSHA:(NSString *)sha error:(NSError **)error; - -// Pushes all references matching `refGlob`. -// -// refGlob - A glob to match references against. This must not be nil. -// error - If not NULL, this will be set to any error that occurs. -// -// Returns whether pushing matching references was successful. -- (BOOL)pushGlob:(NSString *)refGlob error:(NSError **)error; - -// Hides the specified commit and all of its ancestors when enumerating. -// -// sha - The SHA of a commit in the receiver's repository. This must not be -// nil. -// error - If not NULL, this will be set to any error that occurs. -// -// Returns whether marking the SHA for hiding was successful. -- (BOOL)hideSHA:(NSString *)sha error:(NSError **)error; - -// Hides all references matching `refGlob`. -// -// refGlob - A glob to match references against. This must not be nil. -// error - If not NULL, this will be set to any error that occurs. -// -// Returns whether marking matching references for hiding was successful. -- (BOOL)hideGlob:(NSString *)refGlob error:(NSError **)error; - -// Resets the receiver, putting it back into a clean state for reuse, and -// replacing the receiver's `options`. -- (void)resetWithOptions:(GTEnumeratorOptions)options; - -// Enumerates all marked commits, completely exhausting the receiver. -// -// error - If not NULL, set to any error that occurs during traversal. -// -// Returns a (possibly empty) array of GTCommits, or nil if an error occurs. -- (NSArray *)allObjectsWithError:(NSError **)error; - -// Gets the next commit. -// -// success - If not NULL, this will be set to whether getting the next object -// was successful. This will be YES if the receiver is exhausted, so -// it can be used to interpret the meaning of a nil return value. -// error - If not NULL, set to any error that occurs during traversal. -// -// Returns nil if an error occurs or the receiver is exhausted. -- (GTCommit *)nextObjectWithSuccess:(BOOL *)success error:(NSError **)error; - -// Counts the number of commits that were not enumerated, completely exhausting -// the receiver. -// -// error - If not NULL, set to any error that occurs during traversal. -// -// Returns the number of commits remaining, or `NSNotFound` if an error occurs. -- (NSUInteger)countRemainingObjects:(NSError **)error; - -@end diff --git a/Classes/GTIndex.h b/Classes/GTIndex.h deleted file mode 100644 index ca8de4360..000000000 --- a/Classes/GTIndex.h +++ /dev/null @@ -1,206 +0,0 @@ -// -// GTIndex.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 2/28/11. -// -// The MIT License -// -// Copyright (c) 2011 Tim Clem -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#include "git2.h" - -@class GTIndexEntry; -@class GTRepository; -@class GTTree; - -@interface GTIndex : NSObject - -// The repository in which the index resides. This may be nil if the index was -// created with -initWithFileURL:error:. -@property (nonatomic, readonly, strong) GTRepository *repository; - -// The file URL for the index if it exists on disk. -@property (nonatomic, readonly, copy) NSURL *fileURL; - -// The number of entries in the index. -@property (nonatomic, readonly) NSUInteger entryCount; - -// The GTIndexEntries in the index. -@property (nonatomic, readonly, copy) NSArray *entries; - -// Whether the index contains conflicted files. -@property (nonatomic, readonly) BOOL hasConflicts; - -// Creates an in-memory index. -// -// repository - A repository that paths should be relative to. Cannot be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns the newly created index, or nil if an error occurred. -+ (instancetype)inMemoryIndexWithRepository:(GTRepository *)repository error:(NSError **)error; - -// Loads the index at the given file URL. -// -// fileURL - The file URL for the index on disk. Cannot be nil. -// repository - A repository that paths should be relative to. Cannot be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns the loaded index, or nil if an error occurred. -+ (instancetype)indexWithFileURL:(NSURL *)fileURL repository:(GTRepository *)repository error:(NSError **)error; - -// Initializes the receiver with the given libgit2 index. -// -// index - The libgit2 index from which the index should be created. Cannot -// be NULL. -// repository - The repository in which the index resides. Cannot be nil. -// -// Returns the initialized index. -- (instancetype)initWithGitIndex:(git_index *)index repository:(GTRepository *)repository; - -// The underlying `git_index` object. -- (git_index *)git_index __attribute__((objc_returns_inner_pointer)); - -// Refresh the index from the datastore -// -// error - The error if one occurred. -// -// Returns whether the refresh was successful. -- (BOOL)refresh:(NSError **)error; - -// Clear all the entries from the index. This happens in memory. Changes can be -// written to the datastore by calling -write:. -- (void)clear; - -// Get the entry at the given index. -// -// index - The index of the entry to get. Must be within 0 and self.entryCount. -// -// Returns a new GTIndexEntry, or nil if an error occurred. -- (GTIndexEntry *)entryAtIndex:(NSUInteger)index; - -// Get the entry with the given name. -- (GTIndexEntry *)entryWithName:(NSString *)name; - -// Get the entry with the given name. -// -// name - The name of the entry to get. Cannot be nil. -// error - The error if one occurred. -// -// Returns a new GTIndexEntry, or nil if an error occurred. -- (GTIndexEntry *)entryWithName:(NSString *)name error:(NSError **)error; - -// Add an entry to the index. -// -// Note that this *cannot* add submodules. See -[GTSubmodule addToIndex:]. -// -// entry - The entry to add. -// error - The error if one occurred. -// -// Returns YES if successful, NO otherwise. -- (BOOL)addEntry:(GTIndexEntry *)entry error:(NSError **)error; - -// Add an entry (by relative path) to the index. -// Will fail if the receiver's repository is nil. -// -// Note that this *cannot* add submodules. See -[GTSubmodule addToIndex:]. -// -// file - The path (relative to the root of the repository) of the file to add. -// error - The error if one occurred. -// -// Returns YES if successful, NO otherwise. -- (BOOL)addFile:(NSString *)file error:(NSError **)error; - -// Reads the contents of the given tree into the index. -// -// tree - The tree to add to the index. This must not be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns whether reading the tree was successful. -- (BOOL)addContentsOfTree:(GTTree *)tree error:(NSError **)error; - -// Remove an entry (by relative path) from the index. -// Will fail if the receiver's repository is nil. -// -// file - The path (relative to the root of the repository) of the file to -// remove. -// error - The error, if one occurred. -// -// Returns YES if successful, NO otherwise. -- (BOOL)removeFile:(NSString *)file error:(NSError **)error; - -// Write the index to the repository. -// Will fail if the receiver's repository is nil. -// -// error - The error if one occurred. -// -// Returns YES if successful, NO otherwise. -- (BOOL)write:(NSError **)error; - -// Write the index to the repository as a tree. -// Will fail if the receiver's repository is nil. -// -// error - The error if one occurred. -// -// Returns a new GTTree, or nil if an error occurred. -- (GTTree *)writeTree:(NSError **)error; - -// Write the index to the given repository as a tree. -// Will fail if the receiver's index has conflicts. -// -// repository - The repository to write the tree to. Can't be nil. -// error - The error if one occurred. -// -// Returns a new GTTree or nil if an error occurred. -- (GTTree *)writeTreeToRepository:(GTRepository *)repository error:(NSError **)error; - -// Enumerate through any conflicts in the index, running the provided block each -// time. -// -// error - Optionally set in the event of failure. -// block - A block to be run on each conflicted entry. Passed in are index -// entries which represent the common ancestor as well as our and their -// side of the conflict. If the block sets `stop` to YES then the -// iteration will cease once the current block execution has finished. -// Must not be nil. -// -// Returns `YES` in the event of successful enumeration or no conflicts in the -// index, `NO` in case of error. -- (BOOL)enumerateConflictedFilesWithError:(NSError **)error usingBlock:(void (^)(GTIndexEntry *ancestor, GTIndexEntry *ours, GTIndexEntry *theirs, BOOL *stop))block; - -// Update all index entries to match the working directory. -// This method will immediately fail if the index's repo is bare. -// -// pathspecs - An `NSString` array of path patterns. (E.g: *.c) -// If nil is passed in, all index entries will be updated. -// block - A block run each time a pathspec is matched; before the index is updated. -// The `matchedPathspec` parameter is a string indicating what the pathspec (from `pathspecs`) matched. -// If you pass in NULL in to the `pathspecs` parameter this parameter will be empty. -// The `path` parameter is a repository relative path to the file about to be updated. -// The `stop` parameter can be set to `YES` to abort the operation. -// Return `YES` to update the given path, or `NO` to skip it. May be nil. -// error - When something goes wrong, this parameter is set. Optional. -// -// Returns `YES` in the event that everything has gone smoothly. Otherwise, `NO`. -- (BOOL)updatePathspecs:(NSArray *)pathspecs error:(NSError **)error passingTest:(BOOL (^)(NSString *matchedPathspec, NSString *path, BOOL *stop))block; - -@end diff --git a/Classes/GTOID.h b/Classes/GTOID.h deleted file mode 100644 index d51b07385..000000000 --- a/Classes/GTOID.h +++ /dev/null @@ -1,81 +0,0 @@ -// -// GTOID.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 4/9/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "git2.h" -#import "GTObject.h" - -// Represents an object ID. -@interface GTOID : NSObject - -// The SHA pointed to by the OID. -@property (nonatomic, readonly, copy) NSString *SHA; - -// Initializes the receiver with the given git_oid. -// -// git_oid - The underlying git_oid. Cannot be NULL. -// -// Returns the initialized receiver. -- (id)initWithGitOid:(const git_oid *)git_oid; - -// Initializes the receiver by converting the given SHA to an OID. -// -// SHA - The to convert to an OID. Cannot be nil. -// -// Returns the initialized receiver. -- (id)initWithSHA:(NSString *)SHA; - -// Initializes the receiver by converting the given SHA to an OID -// optionally returning a NSError instance on failure. -// -// SHA - The to convert to an OID. Cannot be nil. -// error - Will be filled with an error object in if the SHA cannot be parsed -// -// Returns the initialized receiver or nil if an error occured. -- (id)initWithSHA:(NSString *)SHA error:(NSError **)error; - -// Initializes the receiver by converting the given SHA C string to an OID. -// -// string - The C string to convert. Cannot be NULL. -// -// Returns the initialized receiver. -- (id)initWithSHACString:(const char *)string; - -// Initializes the receiver by converting the given SHA C string to an OID -// optionally returning a NSError instance on failure. -// -// string - The C string to convert. Cannot be NULL. -// error - Will be filled with an error object in if the SHA cannot be parsed -// -// Returns the initialized receiver. -- (id)initWithSHACString:(const char *)string error:(NSError **)error; - -// Creates a new instance with the given git_oid using initWithGitOid: -+ (instancetype)oidWithGitOid:(const git_oid *)git_oid; - -// Creates a new instance from the given SHA string using initWithSHAString: -+ (instancetype)oidWithSHA:(NSString *)SHA; - -// Creates a new instance from the given SHA C string using initWithSHACString: -+ (instancetype)oidWithSHACString:(const char *)SHA; - -// Returns the underlying git_oid struct. -- (const git_oid *)git_oid __attribute__((objc_returns_inner_pointer)); - -@end - -@interface GTOID (GTObjectDatabase) - -// Calculates an OID by hashing the passed data and object type. -// -// data - The data to hash. Cannot be nil. -// type - The type of the git object. -// -// Returns a new OID, or nil if an error occurred. -+ (instancetype)OIDByHashingData:(NSData *)data type:(GTObjectType)type error:(NSError **)error; - -@end diff --git a/Classes/GTObject.h b/Classes/GTObject.h deleted file mode 100644 index fc6ec0b39..000000000 --- a/Classes/GTObject.h +++ /dev/null @@ -1,83 +0,0 @@ -// -// GTObject.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 2/22/11. -// -// The MIT License -// -// Copyright (c) 2011 Tim Clem -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#include "git2.h" - -typedef enum { - GTObjectTypeAny = GIT_OBJ_ANY, /**< Object can be any of the following */ - GTObjectTypeBad = GIT_OBJ_BAD, /**< Object is invalid. */ - GTObjectTypeExt1 = GIT_OBJ__EXT1, /**< Reserved for future use. */ - GTObjectTypeCommit = GIT_OBJ_COMMIT, /**< A commit object. */ - GTObjectTypeTree = GIT_OBJ_TREE, /**< A tree (directory listing) object. */ - GTObjectTypeBlob = GIT_OBJ_BLOB, /**< A file revision object. */ - GTObjectTypeTag = GIT_OBJ_TAG, /**< An annotated tag object. */ - GTObjectTypeExt2 = GIT_OBJ__EXT2, /**< Reserved for future use. */ - GTObjectTypeOffsetDelta = GIT_OBJ_OFS_DELTA,/**< A delta, base is given by an offset. */ - GTObjectTypeRefDelta = GIT_OBJ_REF_DELTA, /**< A delta, base is given by object id. */ -} GTObjectType; - -@class GTRepository; -@class GTOdbObject; -@class GTOID; - -@interface GTObject : NSObject - -@property (nonatomic, readonly) NSString *type; -@property (nonatomic, readonly) NSString *SHA; -@property (nonatomic, readonly) NSString *shortSHA; -@property (nonatomic, readonly, strong) GTRepository *repository; -@property (nonatomic, readonly) GTOID *OID; - -// Convenience initializers -- (id)initWithObj:(git_object *)theObject inRepository:(GTRepository *)theRepo; -+ (id)objectWithObj:(git_object *)theObject inRepository:(GTRepository *)theRepo; - -// The underlying `git_object`. -- (git_object *)git_object __attribute__((objc_returns_inner_pointer)); - -// Read the raw object from the datastore -// -// error(out) - will be filled if an error occurs -// -// returns a GTOdbObject or nil if an error occurred. -- (GTOdbObject *)odbObjectWithError:(NSError **)error; - -// Recursively peel an object until an object of the specified type is met. -// -// type - The type of the requested object. If you pass GTObjectTypeAny -// the object will be peeled until the type changes (e.g. a tag will -// be chased until the referenced object is no longer a tag). -// error - Will be filled with a NSError object on failure. -// May be NULL. -// -// Returns the found object or nil on error. -- (id)objectByPeelingToType:(GTObjectType)type error:(NSError **)error; - -@end - diff --git a/Classes/GTOdbObject.h b/Classes/GTOdbObject.h deleted file mode 100644 index 1f26fe424..000000000 --- a/Classes/GTOdbObject.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// GTOdbObject.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 3/23/11. -// Copyright 2011 GitHub, Inc. All rights reserved. -// - - -#import "GTObject.h" - - -@interface GTOdbObject : NSObject - -// The repository in which the object resides. -@property (nonatomic, readonly, strong) GTRepository *repository; - -// Initializes the object with the underlying libgit2 object and repository. -// -// object - The underlying libgit2 object. Cannot be NULL. -// repository - The repository in which the object resides. Cannot be nil. -// -// Returns the initialized object. -- (id)initWithOdbObj:(git_odb_object *)object repository:(GTRepository *)repository; - -// The underlying `git_odb_object`. -- (git_odb_object *)git_odb_object __attribute__((objc_returns_inner_pointer)); - -- (NSString *)shaHash; -- (GTObjectType)type; -- (size_t)length; -- (NSData *)data; - -// The object ID of this object. -@property (nonatomic, readonly) GTOID *OID; - -@end diff --git a/Classes/GTReference.h b/Classes/GTReference.h deleted file mode 100644 index d71c60277..000000000 --- a/Classes/GTReference.h +++ /dev/null @@ -1,144 +0,0 @@ -// -// GTReference.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 3/2/11. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import "GTObject.h" - -@class GTOID; -@class GTReflog; -@class GTSignature; - -typedef enum { - GTReferenceErrorCodeInvalidReference = -4, -} GTReferenceErrorCode; - -typedef enum { - GTReferenceTypeInvalid = GIT_REF_INVALID, /** Invalid reference */ - GTReferenceTypeOid = GIT_REF_OID, /** A reference which points at an object id */ - GTReferenceTypeSymbolic = GIT_REF_SYMBOLIC, /** A reference which points at another reference */ -} GTReferenceType; - -@class GTRepository; - -/// A git reference object -/// -/// References are considered to be equivalent iff both their `name` and -/// `unresolvedTarget` are equal. -@interface GTReference : NSObject - -@property (nonatomic, readonly, strong) GTRepository *repository; -@property (nonatomic, readonly) GTReferenceType referenceType; -@property (nonatomic, readonly) const git_oid *git_oid; -@property (nonatomic, strong, readonly) GTOID *OID; - -// Whether this is a remote-tracking branch. -@property (nonatomic, readonly, getter = isRemote) BOOL remote; - -// The reflog for the reference. -@property (nonatomic, readonly, strong) GTReflog *reflog; - -// Convenience initializers -+ (id)referenceByLookingUpReferencedNamed:(NSString *)refName inRepository:(GTRepository *)theRepo error:(NSError **)error; -- (id)initByLookingUpReferenceNamed:(NSString *)refName inRepository:(GTRepository *)theRepo error:(NSError **)error; - -+ (id)referenceByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error; -- (id)initByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error; - -- (id)initWithGitReference:(git_reference *)ref repository:(GTRepository *)repository; - -// The underlying `git_reference` object. -- (git_reference *)git_reference __attribute__((objc_returns_inner_pointer)); - -// The target (either GTObject or GTReference) to which the reference points. -@property (nonatomic, readonly, copy) id unresolvedTarget; - -// The resolved object to which the reference points. -@property (nonatomic, readonly, copy) id resolvedTarget; - -// The last direct reference in a chain -@property (nonatomic, readonly, copy) GTReference *resolvedReference; - -// The SHA of the target object -@property (nonatomic, readonly, copy) NSString *targetSHA; - -// Updates the on-disk reference to point to the target and returns the updated -// reference. -// -// Note that this does *not* change the receiver's target. -// -// newTarget - The target for the new reference. This must not be nil. -// signature - A signature for the committer updating this ref, used for -// creating a reflog entry. This may be nil. -// message - A message to use when creating the reflog entry for this action. -// This may be nil. -// error - The error if one occurred. -// -// Returns the updated reference, or nil if an error occurred. -- (GTReference *)referenceByUpdatingTarget:(NSString *)newTarget committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error; - -// The name of the reference. -@property (nonatomic, readonly, copy) NSString *name; - -// Updates the on-disk reference to the name and returns the renamed reference. -// -// Note that this does *not* change the receiver's name. -// -// newName - The new name for the reference. Cannot be nil. -// error - The error if one occurred. -// -// Returns the renamed reference, or nil if an error occurred. -- (GTReference *)referenceByRenaming:(NSString *)newName error:(NSError **)error; - -// Delete this reference. -// -// error - The error if one occurred. -// -// Returns whether the deletion was successful. -- (BOOL)deleteWithError:(NSError **)error; - -// Resolve this reference as a symbolic ref -// -// error(out) - will be filled if an error occurs -// -// returns the peeled GTReference or nil if an error occurred. -- (GTReference *)resolvedReferenceWithError:(NSError **)error; - -// Reload the reference from disk. -// -// error - The error if one occurred. -// -// Returns the reloaded reference, or nil if an error occurred. -- (GTReference *)reloadedReferenceWithError:(NSError **)error; - -// An error indicating that the git_reference is no longer valid. -+ (NSError *)invalidReferenceError; - -// Checks if a reference name is acceptable. -// -// refName - The name to be checked. -// -// Returns YES if the name is valid or NO otherwise. -+ (BOOL)isValidReferenceName:(NSString *)refName; - -@end diff --git a/Classes/GTReflog+Private.h b/Classes/GTReflog+Private.h deleted file mode 100644 index 8c8502470..000000000 --- a/Classes/GTReflog+Private.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// GTReflog+Private.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 4/9/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "GTReflog.h" - -@class GTReference; - -@interface GTReflog () - -// Initializes the receiver with a reference. -// -// reference - The reference whose reflog is being represented. Cannot be nil. -// -// Returns the initialized object. -- (id)initWithReference:(GTReference *)reference; - -@end diff --git a/Classes/GTReflog.h b/Classes/GTReflog.h deleted file mode 100644 index 9d7e6d126..000000000 --- a/Classes/GTReflog.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// GTReflog.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 4/9/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -@class GTSignature; -@class GTReflogEntry; - -// A reflog for a reference. Reflogs should not be created manually. Use -// -[GTReference reflog] to get the reflog for a reference. -@interface GTReflog : NSObject - -// The number of reflog entries. -@property (nonatomic, readonly, assign) NSUInteger entryCount; - -// Writes a new entry to the reflog. -// -// committer - The committer for the reflog entry. Cannot be nil. -// message - The message to associate with the entry. May be nil. -// error - The error if one occurred. -// -// Returns whether the entry was successfully written. -- (BOOL)writeEntryWithCommitter:(GTSignature *)committer message:(NSString *)message error:(NSError **)error; - -// Get the reflog entry at the given index. -// -// index - The reflog entry to get. 0 is the most recent entry. If it is greater -// than `entryCount`, it will assert. -// -// Returns the entry at that index. -- (GTReflogEntry *)entryAtIndex:(NSUInteger)index; - -@end diff --git a/Classes/GTReflogEntry+Private.h b/Classes/GTReflogEntry+Private.h deleted file mode 100644 index d102a891b..000000000 --- a/Classes/GTReflogEntry+Private.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// GTReflogEntry+Private.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 4/9/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "GTReflogEntry.h" - -@interface GTReflogEntry () - -// Initializes the receiver with the underlying reflog entry. -// -// entry - The reflog entry. Cannot be NULL. -// reflog - The reflog in which the entry resides. Cannot be nil. -// -// Returns the initialized object. -- (id)initWithGitReflogEntry:(const git_reflog_entry *)entry reflog:(GTReflog *)reflog; - -@end diff --git a/Classes/GTReflogEntry.h b/Classes/GTReflogEntry.h deleted file mode 100644 index e317b9db0..000000000 --- a/Classes/GTReflogEntry.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// GTReflogEntry.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 4/9/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -@class GTOID; -@class GTSignature; - -// An entry in a GTReflog. -@interface GTReflogEntry : NSObject - -// The OID of the ref before the entry. -@property (nonatomic, readonly, strong) GTOID *previousOID; - -// The OID of the ref when the entry was made. -@property (nonatomic, readonly, strong) GTOID *updatedOID; - -// The person who committed the entry. -@property (nonatomic, readonly, strong) GTSignature *committer; - -// The message associated with the entry. -@property (nonatomic, readonly, copy) NSString *message; - -@end diff --git a/Classes/GTRemote.h b/Classes/GTRemote.h deleted file mode 100644 index 0ef8c8380..000000000 --- a/Classes/GTRemote.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// GTRemote.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 9/12/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "git2.h" - -// A class representing a remote for a git repository. -// -// Analagous to `git_remote` in libgit2. -@interface GTRemote : NSObject - -// Initializes a new GTRemote to represent an underlying `git_remote`. -// -// remote - The underlying `git_remote` object. -- (id)initWithGitRemote:(git_remote *)remote; - -// The underlying `git_remote` object. -- (git_remote *)git_remote __attribute__((objc_returns_inner_pointer)); - -// The name of the remote. -@property (nonatomic, readonly, copy) NSString *name; - -// The URL string for the remote. -@property (nonatomic, readonly, copy) NSString *URLString; - -// The fetch refspecs for this remote. -// -// This array will contain NSStrings of the form -// `+refs/heads/*:refs/remotes/REMOTE/*`. -@property (nonatomic, readonly, copy) NSArray *fetchRefspecs; - -// Updates the URL string for this remote. -// -// URLString - The URLString to update to. May not be nil. -// error - If not NULL, this will be set to any error that occurs when -// updating the URLString or saving the remote. -// -// Returns YES if the URLString was successfully updated, NO and an error -// if updating or saving the remote failed. -- (BOOL)updateURLString:(NSString *)URLString error:(NSError **)error; - -// Adds a fetch refspec to this remote. -// -// fetchRefspec - The fetch refspec string to add. May not be nil. -// error - If not NULL, this will be set to any error that occurs -// when adding the refspec or saving the remote. -// -// Returns YES if there is the refspec is successfully added -// or a matching refspec is already present, NO and an error if -// adding the refspec or saving the remote failed. -- (BOOL)addFetchRefspec:(NSString *)fetchRefspec error:(NSError **)error; - -@end diff --git a/Classes/GTRemote.m b/Classes/GTRemote.m deleted file mode 100644 index a7a3d9778..000000000 --- a/Classes/GTRemote.m +++ /dev/null @@ -1,125 +0,0 @@ -// -// GTRemote.m -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 9/12/12. -// Copyright (c) 2012 GitHub, Inc. All rights reserved. -// - -#import "GTRemote.h" - -#import "NSError+Git.h" -#import "NSArray+StringArray.h" -#import "EXTScope.h" - -@interface GTRemote () - -@property (nonatomic, readonly, assign) git_remote *git_remote; - -@end - -@implementation GTRemote - -#pragma mark Lifecycle - -- (id)initWithGitRemote:(git_remote *)remote { - NSParameterAssert(remote != NULL); - - self = [super init]; - if (self == nil) return nil; - - _git_remote = remote; - - return self; -} - -- (void)dealloc { - if (_git_remote != NULL) git_remote_free(_git_remote); -} - -#pragma mark NSObject - -- (BOOL)isEqual:(GTRemote *)object { - if (object == self) return YES; - if (![object isKindOfClass:[self class]]) return NO; - - return [object.name isEqual:self.name] && [object.URLString isEqual:self.URLString]; -} - -- (NSUInteger)hash { - return self.name.hash ^ self.URLString.hash; -} - -#pragma mark Properties - -- (NSString *)name { - const char *name = git_remote_name(self.git_remote); - if (name == NULL) return nil; - - return @(name); -} - -- (NSString *)URLString { - const char *URLString = git_remote_url(self.git_remote); - if (URLString == NULL) return nil; - - return @(URLString); -} - -- (NSArray *)fetchRefspecs { - __block git_strarray refspecs; - int gitError = git_remote_get_fetch_refspecs(&refspecs, self.git_remote); - if (gitError != GIT_OK) return nil; - - @onExit { - git_strarray_free(&refspecs); - }; - - return [NSArray git_arrayWithStrarray:refspecs]; - -} - -#pragma mark Update the remote - -- (BOOL)saveRemote:(NSError **)error { - int gitError = git_remote_save(self.git_remote); - if (gitError != GIT_OK) { - if (error != NULL) { - *error = [NSError git_errorFor:gitError description:@"Failed to save remote configuration."]; - } - return NO; - } - return YES; -} - -- (BOOL)updateURLString:(NSString *)URLString error:(NSError **)error { - NSParameterAssert(URLString != nil); - - if ([self.URLString isEqualToString:URLString]) return YES; - - int gitError = git_remote_set_url(self.git_remote, URLString.UTF8String); - if (gitError != GIT_OK) { - if (error != NULL) { - *error = [NSError git_errorFor:gitError description:@"Failed to update remote URL string."]; - } - return NO; - } - return [self saveRemote:error]; -} - -- (BOOL)addFetchRefspec:(NSString *)fetchRefspec error:(NSError **)error { - NSParameterAssert(fetchRefspec != nil); - - if ([self.fetchRefspecs containsObject:fetchRefspec]) return YES; - - int gitError = git_remote_add_fetch(self.git_remote, fetchRefspec.UTF8String); - if (gitError != GIT_OK) { - if (error != NULL) { - *error = [NSError git_errorFor:gitError description:@"Failed to add fetch refspec."]; - } - return NO; - } - return [self saveRemote:error]; -} - -@end diff --git a/Classes/GTRepository+Blame.h b/Classes/GTRepository+Blame.h deleted file mode 100644 index 747cb5d38..000000000 --- a/Classes/GTRepository+Blame.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// GTRepository+Blame.h -// ObjectiveGitFramework -// -// Created by Ezekiel Pierson on 2/5/14. -// Copyright (c) 2014 GitHub, Inc. All rights reserved. -// - -#import - -// Enum for options passed to the dictionary in `-blameWithFile:inRepository:options:` -// -// For flag documentation see `blame.h`. -typedef enum { - GTBlameOptionsNormal = GIT_BLAME_NORMAL, -} GTBlameOptions; - -// A `NSNumber` wrapped `GTBlameOptions`. Flags are documented above. -extern NSString * const GTBlameOptionsFlags; - -// A `GTOID` determining the newest commit to consider. -// Default is HEAD. -extern NSString * const GTBlameOptionsNewestCommitOID; - -// A `GTOID` determining the oldest commit to consider. -// Default is the first commit without a parent. -extern NSString * const GTBlameOptionsOldestCommitOID; - -// The first line in the file to blame. Default is 1. -extern NSString * const GTBlameOptionsFirstLine; - -// The last line in the file to blame. Default is the last line. -extern NSString * const GTBlameOptionsLastLine; - -@interface GTRepository (Blame) - -// Create a blame for a file, with options. -// -// path - Path for the file to examine. Can't be nil -// options - A dictionary consiting of the above keys. May be nil. -// error - Populated with an `NSError` object on error. -// -// Returns a new `GTBlame` object or nil if an error occurred. -- (GTBlame *)blameWithFile:(NSString *)path options:(NSDictionary *)options error:(NSError **)error; - -@end diff --git a/Classes/GTRepository+Committing.h b/Classes/GTRepository+Committing.h deleted file mode 100644 index 8566ee338..000000000 --- a/Classes/GTRepository+Committing.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// GTRepository+Committing.h -// ObjectiveGitFramework -// -// Created by Josh Abernathy on 9/30/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "GTRepository.h" - -@interface GTRepository (Committing) - -// Creates a new commit. -// -// tree - The tree used for the commit. Cannot be nil. -// message - The commit message. Cannot be nil. -// author - The author of the commit. Cannot be nil. -// committer - The committer of the commit. Cannot be nil. -// parents - An array of GTCommits. May be nil, which means the commit has no -// parents. -// refName - The ref name which will be updated to point at the new commit. -// May be nil. -// error - The error if one occurred. -// -// Returns the newly created commit, or nil if an error occurred. -- (GTCommit *)createCommitWithTree:(GTTree *)tree message:(NSString *)message author:(GTSignature *)author committer:(GTSignature *)committer parents:(NSArray *)parents updatingReferenceNamed:(NSString *)refName error:(NSError **)error; - -// Creates a new commit using +createCommitWithTree:message:author:committer:parents:updatingReferenceNamed:error: -// with -userSignatureForNow as both the author and committer. -- (GTCommit *)createCommitWithTree:(GTTree *)tree message:(NSString *)message parents:(NSArray *)parents updatingReferenceNamed:(NSString *)refName error:(NSError **)error; - -@end diff --git a/Classes/GTRepository+Private.h b/Classes/GTRepository+Private.h deleted file mode 100644 index 174770d85..000000000 --- a/Classes/GTRepository+Private.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// GTRepository+Private.h -// ObjectiveGitFramework -// -// Created by Etienne on 15/07/13. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -@interface GTRepository () -- (id)lookUpObjectByGitOid:(const git_oid *)oid objectType:(GTObjectType)type error:(NSError **)error; -- (id)lookUpObjectByGitOid:(const git_oid *)oid error:(NSError **)error; -@end diff --git a/Classes/GTRepository+Stashing.h b/Classes/GTRepository+Stashing.h deleted file mode 100644 index 5f31bbf07..000000000 --- a/Classes/GTRepository+Stashing.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// GTRepository+Stashing.h -// ObjectiveGitFramework -// -// Created by Justin Spahr-Summers on 2013-09-27. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "GTRepository.h" - -// Flags for -stashChangesWithMessage:flags:error:. -// Those can be ORed together. See git_stash_flags for additional information. -typedef enum { - GTRepositoryStashFlagDefault = GIT_STASH_DEFAULT, - GTRepositoryStashFlagKeepIndex = GIT_STASH_KEEP_INDEX, - GTRepositoryStashFlagIncludeUntracked = GIT_STASH_INCLUDE_UNTRACKED, - GTRepositoryStashFlagIncludeIgnored = GIT_STASH_INCLUDE_IGNORED -} GTRepositoryStashFlag; - -@interface GTRepository (Stashing) - -// Stash the repository's changes. -// -// message - Message to be attributed to the item in the stash. This may be -// nil. -// stashFlag - The flags of stash to be used. -// error - If not NULL, set to any error that occurred. -// -// Returns a commit representing the stashed changes if successful, or nil -// otherwise. -- (GTCommit *)stashChangesWithMessage:(NSString *)message flags:(GTRepositoryStashFlag)flags error:(NSError **)error; - -// Enumerate over all the stashes in the repository, from most recent to oldest. -// -// block - A block to execute for each stash found. `index` will be the zero-based -// stash index (where 0 is the most recent stash). Setting `stop` to YES -// will cause enumeration to stop after the block returns. -- (void)enumerateStashesUsingBlock:(void (^)(NSUInteger index, NSString *message, GTOID *oid, BOOL *stop))block; - -// Drop a stash from the repository's list of stashes. -// -// index - The index of the stash to drop, where 0 is the most recent stash. -// error - If not NULL, set to any error that occurs. -// -// Returns YES if the stash was successfully dropped, NO otherwise -- (BOOL)dropStashAtIndex:(NSUInteger)index error:(NSError **)error; - -@end diff --git a/Classes/GTRepository+Stashing.m b/Classes/GTRepository+Stashing.m deleted file mode 100644 index 68255caaf..000000000 --- a/Classes/GTRepository+Stashing.m +++ /dev/null @@ -1,61 +0,0 @@ -// -// GTRepository+Stashing.m -// ObjectiveGitFramework -// -// Created by Justin Spahr-Summers on 2013-09-27. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "GTRepository+Stashing.h" -#import "GTOID.h" -#import "GTRepository+Private.h" -#import "GTSignature.h" -#import "NSError+Git.h" - -typedef void (^GTRepositoryStashEnumerationBlock)(NSUInteger index, NSString *message, GTOID *oid, BOOL *stop); - -@implementation GTRepository (Stashing) - -- (GTCommit *)stashChangesWithMessage:(NSString *)message flags:(GTRepositoryStashFlag)flags error:(NSError **)error { - git_oid git_oid; - - int gitError = git_stash_save(&git_oid, self.git_repository, [self userSignatureForNow].git_signature, message.UTF8String, flags); - if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to stash."]; - return nil; - } - - return [self lookUpObjectByGitOid:&git_oid error:error]; -} - -static int stashEnumerationCallback(size_t index, const char *message, const git_oid *stash_id, void *payload) { - GTRepositoryStashEnumerationBlock block = (__bridge GTRepositoryStashEnumerationBlock)payload; - - NSString *messageString = nil; - if (message != NULL) messageString = @(message); - - GTOID *stashOID = [[GTOID alloc] initWithGitOid:stash_id]; - - BOOL stop = NO; - block(index, messageString, stashOID, &stop); - - return (stop ? GIT_EUSER : 0); -} - -- (void)enumerateStashesUsingBlock:(GTRepositoryStashEnumerationBlock)block { - NSParameterAssert(block != nil); - - git_stash_foreach(self.git_repository, &stashEnumerationCallback, (__bridge void *)block); -} - -- (BOOL)dropStashAtIndex:(NSUInteger)index error:(NSError **)error { - int gitError = git_stash_drop(self.git_repository, index); - if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to drop stash."]; - return NO; - } - - return YES; -} - -@end diff --git a/Classes/GTRepository+Status.h b/Classes/GTRepository+Status.h deleted file mode 100644 index 927062ea7..000000000 --- a/Classes/GTRepository+Status.h +++ /dev/null @@ -1,118 +0,0 @@ -// -// GTRepository+Status.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 08/08/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import "GTRepository.h" - -@class GTStatusDelta; - -// An enum representing the status of a file -// See git_status_t -typedef enum { - GTFileStatusCurrent = GIT_STATUS_CURRENT, - - GTFileStatusNewInIndex = GIT_STATUS_INDEX_NEW, - GTFileStatusModifiedInIndex = GIT_STATUS_INDEX_MODIFIED, - GTFileStatusDeletedInIndex = GIT_STATUS_INDEX_DELETED, - GTFileStatusRenamedInIndex = GIT_STATUS_INDEX_RENAMED, - GTFileStatusTypeChangedInIndex = GIT_STATUS_INDEX_TYPECHANGE, - - GTFileStatusNewInWorktree = GIT_STATUS_WT_NEW, - GTFileStatusModifiedInWorktree = GIT_STATUS_WT_MODIFIED, - GTFileStatusDeletedInWorktree = GIT_STATUS_WT_DELETED, - GTFileStatusTypeChangedInWorktree = GIT_STATUS_WT_TYPECHANGE, - GTFileStatusRenamedInWorktree = GIT_STATUS_WT_RENAMED, - - GTFileStatusIgnored = GIT_STATUS_IGNORED, -} GTFileStatusFlags; - -// An `NSNumber` wrapped `GTRepositoryStatusOptionsShow` bitmask. -// -// For extending the reporting of status. Using the flags documented below this -// decides what files are sent when enumerating the status. -extern NSString *const GTRepositoryStatusOptionsShowKey; - -// An enum, for use as documented, with the `GTRepositoryStatusOptionsShowKey` -// key. -// -// See status.h for documentation of each individual flag. -typedef enum { - GTRepositoryStatusOptionsShowIndexAndWorkingDirectory = GIT_STATUS_SHOW_INDEX_AND_WORKDIR, - GTRepositoryStatusOptionsShowIndexOnly = GIT_STATUS_SHOW_INDEX_ONLY, - GTRepositoryStatusOptionsShowWorkingDirectoryOnly = GIT_STATUS_SHOW_WORKDIR_ONLY, -} GTRepositoryStatusOptionsShow; - -// An `NSNumber` wrapped `GTRepositoryStatusOptionsFlags` bitmask containing any -// of the flags documented below. -extern NSString *const GTRepositoryStatusOptionsFlagsKey; - -// An enum, for use as documented, with the `GTRepositoryStatusOptionsFlagsKey` -// key. -// -// See status.h for documentation of each individual flag. -typedef enum { - GTRepositoryStatusFlagsIncludeUntracked = GIT_STATUS_OPT_INCLUDE_UNTRACKED, - GTRepositoryStatusFlagsIncludeIgnored = GIT_STATUS_OPT_INCLUDE_IGNORED, - GTRepositoryStatusFlagsIncludeUnmodified = GIT_STATUS_OPT_INCLUDE_UNMODIFIED, - GTRepositoryStatusFlagsExcludeSubmodules = GIT_STATUS_OPT_EXCLUDE_SUBMODULES, - GTRepositoryStatusFlagsRecurseUntrackedDirectories = GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS, - GTRepositoryStatusFlagsDisablePathspecMatch = GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH, - GTRepositoryStatusFlagsRecurseIgnoredDirectories = GIT_STATUS_OPT_RECURSE_IGNORED_DIRS, - GTRepositoryStatusFlagsRenamesHeadToIndex = GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX, - GTRepositoryStatusFlagsRenamesIndexToWorkingDirectory = GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR, - GTRepositoryStatusFlagsRenamesFromRewrites = GIT_STATUS_OPT_RENAMES_FROM_REWRITES, - GTRepositoryStatusFlagsSortCaseSensitively = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY, - GTRepositoryStatusFlagsSortCaseInsensitively = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY, -} GTRepositoryStatusFlags; - -// An `NSArray` of `NSStrings`s to limit the status to specific paths inside the -// repository. The entries in the array represent either single paths or -// filename patterns with wildcard matching a la standard shell glob (see -// http://linux.die.net/man/7/glob for wildcard matching rules). -// -// Defaults to including all files. -extern NSString *const GTRepositoryStatusOptionsPathSpecArrayKey; - -@interface GTRepository (Status) - -// `YES` if the working directory has no modified, new, or deleted files. -@property (nonatomic, readonly, getter = isWorkingDirectoryClean) BOOL workingDirectoryClean; - -// For each file in the repository, calls your block with the URL of the file -// and the status of that file in the repository. -// -// This will show all file statuses unless a pathspec is specified in the -// options dictionary (using the `GTRepositoryStatusOptionsPathSpecArrayKey` -// key). -// -// options - A dictionary of options using the constants above -// for keys. If no flags are passed in using -// `GTRepositoryStatusOptionsFlagsKey` the defaults of -// GTRepositoryStatusOptionsFlagsIncludeIgnored, -// GTRepositoryStatusOptionsFlagsIncludeUntracked and -// GTRepositoryStatusOptionsFlagsRecurseUntrackedDirectories -// are used. -// error - Will optionally be set in the event of a failure. -// block - The block that gets called for each file. -// `headToIndex` is the delta between the HEAD and -// index. `indexToWorkingDirectory` is the same but -// between the index and the working directory. If -// `stop` is set to `YES`, the iteration will cease -// after the current step. -// Must not be nil. -// -// Returns `NO` in case of a failure or `YES` if the enumeration completed -// successfully. -- (BOOL)enumerateFileStatusWithOptions:(NSDictionary *)options error:(NSError **)error usingBlock:(void (^)(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop))block; - -// Query the status of one file -- (GTFileStatusFlags)statusForFile:(NSString *)filePath success:(BOOL *)success error:(NSError **)error; - -// Should the file be considered as ignored ? -- (BOOL)shouldFileBeIgnored:(NSURL *)fileURL success:(BOOL *)success error:(NSError **)error; - -@end diff --git a/Classes/GTRepository.h b/Classes/GTRepository.h deleted file mode 100644 index e7b9b65ea..000000000 --- a/Classes/GTRepository.h +++ /dev/null @@ -1,450 +0,0 @@ -// -// GTRepository.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 2/17/11. -// -// The MIT License -// -// Copyright (c) 2011 Tim Clem -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - - -#import "GTBranch.h" -#import "GTEnumerator.h" -#import "GTFilterSource.h" -#import "GTObject.h" -#import "GTReference.h" -#import "GTFilterList.h" - -@class GTBlob; -@class GTCommit; -@class GTConfiguration; -@class GTDiffFile; -@class GTIndex; -@class GTObjectDatabase; -@class GTOdbObject; -@class GTSignature; -@class GTSubmodule; -@class GTTag; -@class GTTree; - -// Checkout strategies used by the various -checkout... methods -// See git_checkout_strategy_t -typedef enum { - GTCheckoutStrategyNone = GIT_CHECKOUT_NONE, - GTCheckoutStrategySafe = GIT_CHECKOUT_SAFE, - GTCheckoutStrategySafeCreate = GIT_CHECKOUT_SAFE_CREATE, - GTCheckoutStrategyForce = GIT_CHECKOUT_FORCE, - GTCheckoutStrategyAllowConflicts = GIT_CHECKOUT_ALLOW_CONFLICTS, - GTCheckoutStrategyRemoveUntracked = GIT_CHECKOUT_REMOVE_UNTRACKED, - GTCheckoutStrategyRemoveIgnored = GIT_CHECKOUT_REMOVE_IGNORED, - GTCheckoutStrategyUpdateOnly = GIT_CHECKOUT_UPDATE_ONLY, - GTCheckoutStrategyDontUpdateIndex = GIT_CHECKOUT_DONT_UPDATE_INDEX, - GTCheckoutStrategyNoRefresh = GIT_CHECKOUT_NO_REFRESH, - GTCheckoutStrategyDisablePathspecMatch = GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH, - GTCheckoutStrategySkipLockedDirectories = GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES, -} GTCheckoutStrategyType; - -// Checkout notification flags used by the various -checkout... methods -// See git_checkout_notify_t -typedef enum { - GTCheckoutNotifyNone = GIT_CHECKOUT_NOTIFY_NONE, - GTCheckoutNotifyConflict = GIT_CHECKOUT_NOTIFY_CONFLICT, - GTCheckoutNotifyDirty = GIT_CHECKOUT_NOTIFY_DIRTY, - GTCheckoutNotifyUpdated = GIT_CHECKOUT_NOTIFY_UPDATED, - GTCheckoutNotifyUntracked = GIT_CHECKOUT_NOTIFY_UNTRACKED, - GTCheckoutNotifyIgnored = GIT_CHECKOUT_NOTIFY_IGNORED, - - GTCheckoutNotifyAll = GIT_CHECKOUT_NOTIFY_ALL, -} GTCheckoutNotifyFlags; - -// Transport flags sent as options to +cloneFromURL... method -typedef enum { - GTTransportFlagsNone = GIT_TRANSPORTFLAGS_NONE, - // If you pass this flag and the connection is secured with SSL/TLS, - // the authenticity of the server certificate will not be verified. - GTTransportFlagsNoCheckCert = GIT_TRANSPORTFLAGS_NO_CHECK_CERT, -} GTTransportFlags; - -// An `NSNumber` wrapped `GTTransportFlags`, documented above. -// Default value is `GTTransportFlagsNone`. -extern NSString *const GTRepositoryCloneOptionsTransportFlags; - -// An `NSNumber` wrapped `BOOL`, if YES, create a bare clone. -// Default value is `NO`. -extern NSString *const GTRepositoryCloneOptionsBare; - -// An `NSNumber` wrapped `BOOL`, if NO, don't checkout the remote HEAD. -// Default value is `YES`. -extern NSString *const GTRepositoryCloneOptionsCheckout; - -// A `GTCredentialProvider`, that will be used to authenticate against the remote. -extern NSString *const GTRepositoryCloneOptionsCredentialProvider; - -@interface GTRepository : NSObject - -// The file URL for the repository's working directory. -@property (nonatomic, readonly, strong) NSURL *fileURL; -// The file URL for the repository's .git directory. -@property (nonatomic, readonly, strong) NSURL *gitDirectoryURL; - -// Is this a bare repository (one without a working directory)? -@property (nonatomic, readonly, getter = isBare) BOOL bare; - -// Is this an empty (freshly initialized) repository? -@property (nonatomic, readonly, getter = isEmpty) BOOL empty; - -// Is HEAD detached (not pointing to a branch or tag)? -@property (nonatomic, readonly, getter = isHEADDetached) BOOL HEADDetached; - -// Is HEAD unborn (pointing to a branch without an initial commit)? -@property (nonatomic, readonly, getter = isHEADUnborn) BOOL HEADUnborn; - -// Initializes a new repository at the given file URL. -// -// fileURL - The file URL for the new repository. Cannot be nil. -// error - The error if one occurs. -// -// Returns the initialized repository, or nil if an error occurred. -+ (instancetype)initializeEmptyRepositoryAtFileURL:(NSURL *)fileURL error:(NSError **)error; - -// Initializes a new repository at the given file URL. -// -// fileURL - The file URL for the new repository. Cannot be nil. -// error - The error if one occurs. -// bare - Should the repository be created bare? -// -// Returns the initialized repository, or nil if an error occurred. -+ (instancetype)initializeEmptyRepositoryAtFileURL:(NSURL *)fileURL bare:(BOOL)bare error:(NSError **)error; - -+ (id)repositoryWithURL:(NSURL *)localFileURL error:(NSError **)error; -- (id)initWithURL:(NSURL *)localFileURL error:(NSError **)error; - -// Initializes the receiver to wrap the given repository object. -// -// repository - The repository to wrap. The receiver will take over memory -// management of this object, so it must not be freed elsewhere -// after this method is invoked. This must not be nil. -// -// Returns an initialized GTRepository. -- (id)initWithGitRepository:(git_repository *)repository; - -// The underlying `git_repository` object. -- (git_repository *)git_repository __attribute__((objc_returns_inner_pointer)); - -// Clone a repository -// -// originURL - The URL to clone from. -// workdirURL - A URL to the desired working directory on the local machine. -// options - A dictionary consisting of the options: -// `GTRepositoryCloneOptionsTransportFlags`, -// `GTRepositoryCloneOptionsBare`, and `GTRepositoryCloneOptionsCheckout`. -// error - A pointer to fill in case of trouble. -// transferProgressBlock - This block is called with network transfer updates. -// checkoutProgressBlock - This block is called with checkout updates -// (if `GTRepositoryCloneOptionsCheckout` is YES). -// -// returns nil (and fills the error parameter) if an error occurred, or a GTRepository object if successful. -+ (id)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL options:(NSDictionary *)options error:(NSError **)error transferProgressBlock:(void (^)(const git_transfer_progress *))transferProgressBlock checkoutProgressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))checkoutProgressBlock; - -// Lookup objects in the repo by oid or sha1 -- (id)lookUpObjectByOID:(GTOID *)oid objectType:(GTObjectType)type error:(NSError **)error; -- (id)lookUpObjectByOID:(GTOID *)oid error:(NSError **)error; -- (id)lookUpObjectBySHA:(NSString *)sha objectType:(GTObjectType)type error:(NSError **)error; -- (id)lookUpObjectBySHA:(NSString *)sha error:(NSError **)error; - -// Lookup an object in the repo using a revparse spec -- (id)lookUpObjectByRevParse:(NSString *)spec error:(NSError **)error; - -// Finds the branch with the given name and type. -// -// branchName - The name of the branch to look up (e.g., `master` or -// `origin/master`). This must not be nil. -// branchType - Whether the branch to look up is local or remote. -// success - If not NULL, set to whether the branch lookup finished without -// any errors. This can be `YES` even if no matching branch is -// found. -// error - If not NULL, set to any error that occurs. -// -// Returns the matching branch, or nil if no match was found or an error occurs. -// The latter two cases can be distinguished by checking `success`. -- (GTBranch *)lookUpBranchWithName:(NSString *)branchName type:(GTBranchType)branchType success:(BOOL *)success error:(NSError **)error; - -// List all references in the repository -// -// repository - The GTRepository to list references in -// error(out) - will be filled if an error occurs -// -// returns an array of NSStrings holding the names of the references -// returns nil if an error occurred and fills the error parameter -- (NSArray *)referenceNamesWithError:(NSError **)error; - -- (GTReference *)headReferenceWithError:(NSError **)error; - -// Convenience methods to return branches in the repository -- (NSArray *)allBranchesWithError:(NSError **)error; - -- (NSArray *)localBranchesWithError:(NSError **)error; -- (NSArray *)remoteBranchesWithError:(NSError **)error; -- (NSArray *)branchesWithPrefix:(NSString *)prefix error:(NSError **)error; - -// Convenience method to return all tags in the repository -- (NSArray *)allTagsWithError:(NSError **)error; - -// Count all commits in the current branch (HEAD) -// -// error(out) - will be filled if an error occurs -// -// returns number of commits in the current branch or NSNotFound if an error occurred -- (NSUInteger)numberOfCommitsInCurrentBranch:(NSError **)error; - -// Creates a direct reference to the given OID. -// -// name - The full name for the new reference. This must not be nil. -// targetOID - The OID that the new ref should point to. This must not be nil. -// signature - A signature for the committer creating this ref, used for -// creating a reflog entry. This may be nil. -// message - A message to use when creating the reflog entry for this action. -// This may be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns the created ref, or nil if an error occurred. -- (GTReference *)createReferenceNamed:(NSString *)name fromOID:(GTOID *)targetOID committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error; - -// Creates a symbolic reference to another ref. -// -// name - The full name for the new reference. This must not be nil. -// targetRef - The ref that the new ref should point to. This must not be nil. -// signature - A signature for the committer creating this ref, used for -// creating a reflog entry. This may be nil. -// message - A message to use when creating the reflog entry for this action. -// This may be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns the created ref, or nil if an error occurred. -- (GTReference *)createReferenceNamed:(NSString *)name fromReference:(GTReference *)targetRef committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error; - -// Create a new local branch pointing to the given OID. -// -// name - The name for the new branch (e.g., `master`). This must not be -// nil. -// targetOID - The OID to create the new branch off. This must not be nil. -// signature - A signature for the committer creating this branch, used for -// creating a reflog entry. This may be nil. -// message - A message to use when creating the reflog entry for this action. -// This may be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns the new branch, or nil if an error occurred. -- (GTBranch *)createBranchNamed:(NSString *)name fromOID:(GTOID *)targetOID committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error; - -// Get the current branch. -// -// error(out) - will be filled if an error occurs -// -// returns the current branch or nil if an error occurred. -- (GTBranch *)currentBranchWithError:(NSError **)error; - -// Find the commits that are on our local branch but not on the remote branch. -// -// error(out) - will be filled if an error occurs -// -// returns the local commits, an empty array if there is no remote branch, or nil if an error occurred -- (NSArray *)localCommitsRelativeToRemoteBranch:(GTBranch *)remoteBranch error:(NSError **)error; - -// Retrieves git's "prepared message" for the next commit, like the default -// message pre-filled when committing after a conflicting merge. -// -// error - If not NULL, set to any error that occurs. -// -// Returns the message from disk, or nil if no prepared message exists or an -// error occurred. -- (NSString *)preparedMessageWithError:(NSError **)error; - -// The signature for the user at the current time, based on the repository and -// system configs. If the user's name or email have not been set, reasonable -// defaults will be used instead. Will never return nil. -// -// Returns the signature. -- (GTSignature *)userSignatureForNow; - -// Reloads all cached information about the receiver's submodules. -// -// Existing GTSubmodule objects from this repository will be mutated as part of -// this operation. -// -// error - If not NULL, set to any errors that occur. -// -// Returns whether the reload succeeded. -- (BOOL)reloadSubmodules:(NSError **)error; - -// Enumerates over all the tracked submodules in the repository. -// -// recursive - Whether to recurse into nested submodules, depth-first. -// block - A block to execute for each `submodule` found. If an error -// occurred while reading the submodule, `submodule` will be nil and -// `error` will contain the error information. Setting `stop` to YES -// will cause enumeration to stop after the block returns. This must -// not be nil. -- (void)enumerateSubmodulesRecursively:(BOOL)recursive usingBlock:(void (^)(GTSubmodule *submodule, NSError *error, BOOL *stop))block; - -// Looks up the top-level submodule with the given name. This will not recurse -// into submodule repositories. -// -// name - The name of the submodule. This must not be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns the first submodule that matches the given name, or nil if an error -// occurred locating or instantiating the GTSubmodule. -- (GTSubmodule *)submoduleWithName:(NSString *)name error:(NSError **)error; - -// Finds the merge base between the commits pointed at by the given OIDs. -// -// firstOID - The OID for the first commit. This must not be nil. -// secondOID - The OID for the second commit. This must not be nil. -// error - If not NULL, set to any error that occurs. -// -// Returns the merge base, or nil if none is found or an error occurred. -- (GTCommit *)mergeBaseBetweenFirstOID:(GTOID *)firstOID secondOID:(GTOID *)secondOID error:(NSError **)error; - -// The object database backing the repository. -// -// error - The error if one occurred. -// -// Returns the object database, or nil if an error occurred. -- (GTObjectDatabase *)objectDatabaseWithError:(NSError **)error; - -// The configuration for the repository. -// -// error - The error if one occurred. -// -// Returns the configuration, or nil if an error occurred. -- (GTConfiguration *)configurationWithError:(NSError **)error; - -// The index for the repository. -// -// error - The error if one occurred. -// -// Returns the index, or nil if an error occurred. -- (GTIndex *)indexWithError:(NSError **)error; - -// Creates a new lightweight tag in this repository. -// -// name - Name for the tag; this name is validated -// for consistency. It should also not conflict with an -// already existing tag name -// target - Object to which this tag points. This object -// must belong to this repository. -// error - Will be filled with a NSError instance on failuer. -// May be NULL. -// -// Returns YES on success or NO otherwise. -- (BOOL)createLightweightTagNamed:(NSString *)tagName target:(GTObject *)target error:(NSError **)error; - -// Creates an annotated tag in this repo. Existing tags are not overwritten. -// -// tagName - Name for the tag; this name is validated -// for consistency. It should also not conflict with an -// already existing tag name -// theTarget - Object to which this tag points. This object -// must belong to this repository. -// tagger - Signature of the tagger for this tag, and -// of the tagging time -// message - Full message for this tag -// error - Will be filled with a NSError object in case of error. -// May be NULL. -// -// Returns the object ID of the newly created tag or nil on error. -- (GTOID *)OIDByCreatingTagNamed:(NSString *)tagName target:(GTObject *)theTarget tagger:(GTSignature *)theTagger message:(NSString *)theMessage error:(NSError **)error; - -// Creates an annotated tag in this repo. Existing tags are not overwritten. -// -// tagName - Name for the tag; this name is validated -// for consistency. It should also not conflict with an -// already existing tag name -// theTarget - Object to which this tag points. This object -// must belong to this repository. -// tagger - Signature of the tagger for this tag, and -// of the tagging time -// message - Full message for this tag -// error - Will be filled with a NSError object in case of error. -// May be NULL. -// -// Returns the newly created tag or nil on error. -- (GTTag *)createTagNamed:(NSString *)tagName target:(GTObject *)theTarget tagger:(GTSignature *)theTagger message:(NSString *)theMessage error:(NSError **)error; - -// Checkout a commit -// -// targetCommit - The commit to checkout. -// strategy - The checkout strategy to use. -// notifyFlags - Flags that indicate which notifications should cause `notifyBlock` -// to be called. -// error - The error if one occurred. Can be NULL. -// notifyBlock - The block to call back for notification handling. Can be nil. -// progressBlock - The block to call back for progress updates. Can be nil. -// -// Returns YES if operation was successful, NO otherwise -- (BOOL)checkoutCommit:(GTCommit *)targetCommit strategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags error:(NSError **)error progressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))progressBlock notifyBlock:(int (^)(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir))notifyBlock; - -// Checkout a reference -// -// targetCommit - The reference to checkout. -// strategy - The checkout strategy to use. -// notifyFlags - Flags that indicate which notifications should cause `notifyBlock` -// to be called. -// error - The error if one occurred. Can be NULL. -// notifyBlock - The block to call back for notification handling. Can be nil. -// progressBlock - The block to call back for progress updates. Can be nil. -// -// Returns YES if operation was successful, NO otherwise -- (BOOL)checkoutReference:(GTReference *)targetReference strategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags error:(NSError **)error progressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))progressBlock notifyBlock:(int (^)(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir))notifyBlock; - -// Convenience wrapper for checkoutCommit:strategy:notifyFlags:error:notifyBlock:progressBlock without notifications -- (BOOL)checkoutCommit:(GTCommit *)target strategy:(GTCheckoutStrategyType)strategy error:(NSError **)error progressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))progressBlock; - -// Convenience wrapper for checkoutReference:strategy:notifyFlags:error:notifyBlock:progressBlock without notifications -- (BOOL)checkoutReference:(GTReference *)target strategy:(GTCheckoutStrategyType)strategy error:(NSError **)error progressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))progressBlock; - -/// Flush the gitattributes cache. -- (void)flushAttributesCache; - -/// Loads the filter list for a given path in the repository. -/// -/// path - The path to load filters for. This is used to determine which -/// filters to apply, and does not necessarily need to point to a file -/// that already exists. This must not be nil. -/// blob - The blob to which the filter will be applied, if known. This is -/// used to determine which filters to apply, and can differ from the -/// content of the file at `path`. This may be nil. -/// mode - The direction in which the data will be filtered. -/// options - The list options. See the libgit2 header for more information. -/// success - If not NULL, set to `NO` if an error occurs. If `nil` is -/// returned and this argument is set to `YES`, there were no filters -/// to apply. -/// error - If not NULL, set to any error that occurs. -/// -/// Returns the loaded filter list, or nil if an error occurs or there are no -/// filters to apply to the given path. The latter two cases can be -/// distinguished using the value of `success`. -- (GTFilterList *)filterListWithPath:(NSString *)path blob:(GTBlob *)blob mode:(GTFilterSourceMode)mode options:(GTFilterListOptions)options success:(BOOL *)success error:(NSError **)error; - -@end diff --git a/Classes/GTStatusDelta.h b/Classes/GTStatusDelta.h deleted file mode 100644 index 8c4fbe1b7..000000000 --- a/Classes/GTStatusDelta.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// GTStatusDelta.h -// ObjectiveGitFramework -// -// Created by Danny Greg on 08/08/2013. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import - -#import "git2.h" - -@class GTDiffFile; - -// An enum representing the status of the file. -// -// See diff.h for documentation of individual flags. -typedef enum { - GTStatusDeltaStatusUnmodified = GIT_DELTA_UNMODIFIED, - GTStatusDeltaStatusAdded = GIT_DELTA_ADDED, - GTStatusDeltaStatusDeleted = GIT_DELTA_DELETED, - GTStatusDeltaStatusModified = GIT_DELTA_MODIFIED, - GTStatusDeltaStatusRenamed = GIT_DELTA_RENAMED, - GTStatusDeltaStatusCopied = GIT_DELTA_COPIED, - GTStatusDeltaStatusIgnored = GIT_DELTA_IGNORED, - GTStatusDeltaStatusUntracked = GIT_DELTA_UNTRACKED, - GTStatusDeltaStatusTypeChange = GIT_DELTA_TYPECHANGE, -} GTStatusDeltaStatus; - -// Represents the status of a file in a repository. -@interface GTStatusDelta : NSObject - -// The file as it was prior to the change represented by this status delta. -@property (nonatomic, readonly, copy) GTDiffFile *oldFile; - -// The file after the change represented by this status delta -@property (nonatomic, readonly, copy) GTDiffFile *newFile __attribute__((ns_returns_not_retained)); - -// The status of the file. -@property (nonatomic, readonly) GTStatusDeltaStatus status; - -// A float between 0 and 1 describing how similar the old and new -// files are (where 0 is not at all and 1 is identical). -// -// Only useful when the status is `GTStatusDeltaStatusRenamed` or -// `GTStatusDeltaStatusCopied`. -@property (nonatomic, readonly) double similarity; - -// Designated initializer. -- (instancetype)initWithGitDiffDelta:(const git_diff_delta *)delta; - -@end diff --git a/Classes/GTSubmodule.h b/Classes/GTSubmodule.h deleted file mode 100644 index 7b028de26..000000000 --- a/Classes/GTSubmodule.h +++ /dev/null @@ -1,147 +0,0 @@ -// -// GTSubmodule.h -// ObjectiveGitFramework -// -// Created by Justin Spahr-Summers on 2013-05-29. -// Copyright (c) 2013 GitHub, Inc. All rights reserved. -// - -#import -#import "GTObject.h" - -@class GTOID; - -// Determines which kinds of changes within the submodule repository will be -// ignored when retrieving its status. -// -// These flags are mutually exclusive. -typedef enum { - GTSubmoduleIgnoreReset = GIT_SUBMODULE_IGNORE_RESET, - GTSubmoduleIgnoreNone = GIT_SUBMODULE_IGNORE_NONE, - GTSubmoduleIgnoreUntracked = GIT_SUBMODULE_IGNORE_UNTRACKED, - GTSubmoduleIgnoreDirty = GIT_SUBMODULE_IGNORE_DIRTY, - GTSubmoduleIgnoreAll = GIT_SUBMODULE_IGNORE_ALL -} GTSubmoduleIgnoreRule; - -// Describes the status of a submodule. -// -// These flags may be ORed together. -typedef enum { - GTSubmoduleStatusUnknown = 0, - - GTSubmoduleStatusExistsInHEAD = GIT_SUBMODULE_STATUS_IN_HEAD, - GTSubmoduleStatusExistsInIndex = GIT_SUBMODULE_STATUS_IN_INDEX, - GTSubmoduleStatusExistsInConfig = GIT_SUBMODULE_STATUS_IN_CONFIG, - GTSubmoduleStatusExistsInWorkingDirectory = GIT_SUBMODULE_STATUS_IN_WD, - - GTSubmoduleStatusAddedToIndex = GIT_SUBMODULE_STATUS_INDEX_ADDED, - GTSubmoduleStatusDeletedFromIndex = GIT_SUBMODULE_STATUS_INDEX_DELETED, - GTSubmoduleStatusModifiedInIndex = GIT_SUBMODULE_STATUS_INDEX_MODIFIED, - - GTSubmoduleStatusUninitialized = GIT_SUBMODULE_STATUS_WD_UNINITIALIZED, - GTSubmoduleStatusAddedToWorkingDirectory = GIT_SUBMODULE_STATUS_WD_ADDED, - GTSubmoduleStatusDeletedFromWorkingDirectory = GIT_SUBMODULE_STATUS_WD_DELETED, - GTSubmoduleStatusModifiedInWorkingDirectory = GIT_SUBMODULE_STATUS_WD_MODIFIED, - - GTSubmoduleStatusDirtyIndex = GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED, - GTSubmoduleStatusDirtyWorkingDirectory = GIT_SUBMODULE_STATUS_WD_WD_MODIFIED, - GTSubmoduleStatusUntrackedFilesInWorkingDirectory = GIT_SUBMODULE_STATUS_WD_UNTRACKED -} GTSubmoduleStatus; - -// Represents a submodule within its parent repository. -@interface GTSubmodule : NSObject - -// The repository that this submodule lives within. -@property (nonatomic, strong, readonly) GTRepository *parentRepository; - -// The current ignore rule for this submodule. -// -// Setting this property will only update the rule in memory, not on disk. -@property (nonatomic, assign) GTSubmoduleIgnoreRule ignoreRule; - -// The OID that the submodule is pinned to in the parent repository's index. -// -// If the submodule is not in the index, this will be nil. -@property (nonatomic, strong, readonly) GTOID *indexOID; - -// The OID that the submodule is pinned to in the parent repository's HEAD -// commit. -// -// If the submodule is not in HEAD, this will be nil. -@property (nonatomic, strong, readonly) GTOID *HEADOID; - -// The OID that is checked out in the submodule repository. -// -// If the submodule is not checked out, this will be nil. -@property (nonatomic, strong, readonly) GTOID *workingDirectoryOID; - -// The name of this submodule. -@property (nonatomic, copy, readonly) NSString *name; - -// The path to this submodule, relative to its parent repository's root. -@property (nonatomic, copy, readonly) NSString *path; - -// The remote URL provided for this submodule, read from the parent repository's -// `.git/config` or `.gitmodules` file. -@property (nonatomic, copy, readonly) NSString *URLString; - -// Initializes the receiver to wrap the given submodule object. -// -// submodule - The submodule to wrap. The receiver will not own this object, so -// it must not be freed while the GTSubmodule is alive. This must -// not be NULL. -// repository - The repository that contains the submodule. This must not be -// nil. -// -// Returns an initialized GTSubmodule, or nil if an error occurs. -- (id)initWithGitSubmodule:(git_submodule *)submodule parentRepository:(GTRepository *)repository; - -// The underlying `git_submodule` object. -- (git_submodule *)git_submodule __attribute__((objc_returns_inner_pointer)); - -// Reloads the receiver's configuration from the parent repository. -// -// This will mutate properties on the receiver. -// -// Returns whether reloading succeeded. -- (BOOL)reload:(NSError **)error; - -// Synchronizes the submodule repository's configuration files with the settings -// from the parent repository. -// -// Returns whether the synchronization succeeded. -- (BOOL)sync:(NSError **)error; - -// Opens the submodule repository. -// -// If the submodule is not currently checked out, this will fail. -// -// Returns the opened repository, or nil if an error occurs. -- (GTRepository *)submoduleRepository:(NSError **)error; - -// Determines the status for the submodule. -// -// Returns the status, or `GTSubmoduleStatusUnknown` if an error occurs. -- (GTSubmoduleStatus)status:(NSError **)error; - -// Initializes the submodule by copying its information into the parent -// repository's `.git/config` file. This is equivalent to `git submodule init` -// on the command line. -// -// overwrite - Whether to force an update to the `.git/config` file. If NO, -// existing entries will not be overwritten. -// error - If not NULL, set to any error that occurs. -// -// Returns whether the initialization succeeded. -- (BOOL)writeToParentConfigurationDestructively:(BOOL)overwrite error:(NSError **)error; - -/// Add the current HEAD to the parent repository's index. -/// -/// Note that it does *not* write the index. -/// -/// error - The error if one occurred. -/// -/// Returns whether the add was successful. -- (BOOL)addToIndex:(NSError **)error; - -@end diff --git a/Classes/GTTree.h b/Classes/GTTree.h deleted file mode 100644 index 0047a6f2d..000000000 --- a/Classes/GTTree.h +++ /dev/null @@ -1,93 +0,0 @@ -// -// GTTree.h -// ObjectiveGitFramework -// -// Created by Timothy Clem on 2/22/11. -// -// The MIT License -// -// Copyright (c) 2011 Tim Clem -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - - -#import "GTObject.h" - -@class GTTreeEntry; -@class GTIndex; - -typedef enum GTTreeEnumerationOptions { - GTTreeEnumerationOptionPre = GIT_TREEWALK_PRE, // Walk the tree in pre-order (subdirectories come first) - GTTreeEnumerationOptionPost = GIT_TREEWALK_POST, // Walk the tree in post-order (subdirectories come last) -} GTTreeEnumerationOptions; - -@interface GTTree : GTObject - -// The number of entries in the tree. -@property (nonatomic, readonly) NSUInteger entryCount; - -// The contents of the tree, as an array of whose objects are of type `GTTreeEntry` -@property (nonatomic, strong, readonly) NSArray *entries; - -// The underlying `git_object` as a `git_tree` object. -- (git_tree *)git_tree __attribute__((objc_returns_inner_pointer)); - -// Get a entry at the specified index -// -// index - index to retreive entry from -// -// returns a GTTreeEntry or nil if there is nothing at the index -- (GTTreeEntry *)entryAtIndex:(NSUInteger)index; - -// Get a entry by name -// -// name - the name of the entry -// -// returns a GTTreeEntry or nil if there is nothing with the specified name -- (GTTreeEntry *)entryWithName:(NSString *)name; - -// Enumerates the contents of the tree -// -// options - One of `GTTreeEnumerationOptionPre` (for pre-order walks) or -// `GTTreeEnumerationOptionPost` (for post-order walks). -// error - The error if one occurred. -// block - A block that will be invoked with the current entry, a -// (repository relative) path to the root of the current entry, -// and a stop parameter to abort the walk. Cannot be nil. -// Return `YES` to move into the descendants of the entry. -// Return `NO` to skip the entry's descendants. -// Returning `YES` or `NO` only matters when in pre-order mode. -// -// Returns `YES` if the enumeration completed successfully, `NO` otherwise. -- (BOOL)enumerateEntriesWithOptions:(GTTreeEnumerationOptions)options error:(NSError **)error block:(BOOL (^)(GTTreeEntry *entry, NSString *root, BOOL *stop))block; - -// Merges the given tree into the receiver in memory and produces the result as -// an index. -// -// otherTree - The tree with which the receiver should be merged. Cannot be -// nil. -// ancestorTree - The common ancestor of the two trees, or nil if none. -// error - The error if one occurred. -// -// Returns an index which represents the result of the merge, or nil if an error -// occurred. -- (GTIndex *)merge:(GTTree *)otherTree ancestor:(GTTree *)ancestorTree error:(NSError **)error; - -@end diff --git a/Classes/GTTreeBuilder.h b/Classes/GTTreeBuilder.h deleted file mode 100644 index 4707d273a..000000000 --- a/Classes/GTTreeBuilder.h +++ /dev/null @@ -1,127 +0,0 @@ -// -// GTTreeBuilder.h -// ObjectiveGitFramework -// -// Created by Johnnie Walker on 17/05/2013. -// -// The MIT License -// -// Copyright (c) 2013 Johnnie Walker -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import -#include "git2.h" - -// The mode of an index or tree entry. -typedef enum { - GTFileModeNew = GIT_FILEMODE_NEW, - GTFileModeTree = GIT_FILEMODE_TREE, - GTFileModeBlob = GIT_FILEMODE_BLOB, - GTFileModeBlobExecutable = GIT_FILEMODE_BLOB_EXECUTABLE, - GTFileModeLink = GIT_FILEMODE_LINK, - GTFileModeCommit = GIT_FILEMODE_COMMIT -} GTFileMode; - -@class GTTree; -@class GTTreeEntry; -@class GTRepository; -@class GTOID; - -// A tree builder is used to create or modify trees in memory and write them as -// tree objects to a repository. -@interface GTTreeBuilder : NSObject - -// Get the number of entries listed in a treebuilder. -@property (nonatomic, readonly) NSUInteger entryCount; - -// Initializes the receiver, optionally from an existing tree. -// -// treeOrNil - Source tree (or nil) -// error - The error if one occurred. -// -// Returns the initialized object, or nil if an error occurred. -- (id)initWithTree:(GTTree *)treeOrNil error:(NSError **)error; - -// The underlying `git_treebuilder` object. -- (git_treebuilder *)git_treebuilder __attribute__((objc_returns_inner_pointer)); - -// Clear all the entires in the builder. -- (void)clear; - -// Filter the entries in the tree. -// -// filterBlock - A block which returns YES for entries which should be filtered -// from the index. -- (void)filter:(BOOL (^)(const git_tree_entry *entry))filterBlock; - -// Get an entry from the builder from its file name. -// -// fileName - File name for the object in the index. Cannot be nil. -// -// Returns the matching entry or nil if it doesn't exist. -- (GTTreeEntry *)entryWithFileName:(NSString *)fileName; - -// Adds or updates the entry for the file name with the given data. When the -// tree is written, a blob will be inserted into the object database containing -// `data`. -// -// data - The data for the entry. Cannot be nil. -// fileName - The file name for the object. Cannot be nil. -// fileMode - The file mode for the object. -// error - The error if one occurred. -// -// Returns the added entry, or nil if an error occurred -- (GTTreeEntry *)addEntryWithData:(NSData *)data fileName:(NSString *)fileName fileMode:(GTFileMode)fileMode error:(NSError **)error; - -// Add or update an entry to the builder. -// -// oid - The OID of a git object aleady stored in the repository. -// fileName - File name for the object in the index. Cannot be nil. -// fileMode - File mode for the object in the index. -// error - The error if one occurred. -// -// If an entry named `filename` already exists, its attributes will be updated -// with the given ones. -// -// No attempt is made to ensure that the provided oid points to an existing git -// object in the object database, nor that the attributes make sense regarding -// the type of the pointed at object. -// -// Returns the added entry, or nil if an error occurred. -- (GTTreeEntry *)addEntryWithOID:(GTOID *)oid fileName:(NSString *)fileName fileMode:(GTFileMode)fileMode error:(NSError **)error; - -// Remove an entry from the builder by its file name. -// -// fileName - File name for the object in the tree. -// error - The error if one occurred. -// -// Returns YES if the entry was removed, or NO if an error occurred. -- (BOOL)removeEntryWithFileName:(NSString *)fileName error:(NSError **)error; - -// Write the contents of the tree builder as a tree object. -// -// repository - Repository in which to write the tree. -// error - The error if one occurred. -// -// Returns the written tree, or nil if an error occurred. -- (GTTree *)writeTreeToRepository:(GTRepository *)repository error:(NSError **)error; - -@end diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings deleted file mode 100644 index 88f65cf6e..000000000 --- a/English.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/External/Configuration b/External/Configuration deleted file mode 160000 index bbb2bee4e..000000000 --- a/External/Configuration +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bbb2bee4eb75fcc05357ed78621f3424f8588e8e diff --git a/External/libcrypto.a b/External/libcrypto.a deleted file mode 120000 index fe603c877..000000000 --- a/External/libcrypto.a +++ /dev/null @@ -1 +0,0 @@ -/usr/local/opt/openssl/lib/libcrypto.a \ No newline at end of file diff --git a/External/libextobjc/extobjc/EXTScope.h b/External/libextobjc/extobjc/EXTScope.h index c835438cd..5c68469f6 100644 --- a/External/libextobjc/extobjc/EXTScope.h +++ b/External/libextobjc/extobjc/EXTScope.h @@ -88,7 +88,7 @@ _Pragma("clang diagnostic pop") /*** implementation details follow ***/ -typedef void (^gt_ext_cleanupBlock_t)(); +typedef void (^gt_ext_cleanupBlock_t)(void); void gt_ext_executeCleanupBlock (__strong gt_ext_cleanupBlock_t *block); diff --git a/External/libgit2 b/External/libgit2 index 9331f98ac..1a107fac0 160000 --- a/External/libgit2 +++ b/External/libgit2 @@ -1 +1 @@ -Subproject commit 9331f98acaffd377a8076ab111bed84ff89e8e72 +Subproject commit 1a107fac0fc88a4d74b64ffc9ae2fd178ba631c0 diff --git a/External/libssh2 b/External/libssh2 index f1cfa55b6..f15b1e297 160000 --- a/External/libssh2 +++ b/External/libssh2 @@ -1 +1 @@ -Subproject commit f1cfa55b6064ba18fc0005713ed790da579361b5 +Subproject commit f15b1e297f72882214988101ccdc5e6ad30d7e6e diff --git a/External/libssl.a b/External/libssl.a deleted file mode 120000 index e321f3268..000000000 --- a/External/libssl.a +++ /dev/null @@ -1 +0,0 @@ -/usr/local/opt/openssl/lib/libssl.a \ No newline at end of file diff --git a/External/openssl b/External/openssl index 46ebd9e3b..e71ebf275 160000 --- a/External/openssl +++ b/External/openssl @@ -1 +1 @@ -Subproject commit 46ebd9e3bb623d3c15ef2203038956f3f7213620 +Subproject commit e71ebf275da66dfd601c92e0e80a35114c32f6f8 diff --git a/Info.plist b/Info.plist index 19190fb41..d3de8eefb 100644 --- a/Info.plist +++ b/Info.plist @@ -3,25 +3,23 @@ CFBundleDevelopmentRegion - English + en CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - + $(EXECUTABLE_NAME) CFBundleIdentifier - com.github.${PRODUCT_NAME:rfc1034Identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName - ${PRODUCT_NAME} + $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString - 0.1 + 1.0 CFBundleSignature ???? CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/LICENSE b/LICENSE index 5da811ff5..f9769cde8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2012 libgit2 contributors +Copyright (c) 2016 libgit2 contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ObjectiveGit.modulemap b/ObjectiveGit.modulemap new file mode 100644 index 000000000..3ef32bc58 --- /dev/null +++ b/ObjectiveGit.modulemap @@ -0,0 +1,146 @@ +framework module ObjectiveGit { + umbrella header "ObjectiveGit.h" + + header "git2/attr.h" + header "git2/blob.h" + header "git2/blame.h" + header "git2/branch.h" + header "git2/buffer.h" + header "git2/checkout.h" + header "git2/cherrypick.h" + header "git2/clone.h" + header "git2/commit.h" + header "git2/common.h" + header "git2/config.h" + header "git2/describe.h" + header "git2/diff.h" + header "git2/errors.h" + header "git2/filter.h" + header "git2/graph.h" + header "git2/ignore.h" + header "git2/index.h" + header "git2/indexer.h" + header "git2/merge.h" + header "git2/message.h" + header "git2/net.h" + header "git2/notes.h" + header "git2/object.h" + header "git2/odb.h" + header "git2/odb_backend.h" + header "git2/oid.h" + header "git2/pack.h" + header "git2/patch.h" + header "git2/pathspec.h" + header "git2/refdb.h" + header "git2/reflog.h" + header "git2/refs.h" + header "git2/refspec.h" + header "git2/remote.h" + header "git2/repository.h" + header "git2/reset.h" + header "git2/revert.h" + header "git2/revparse.h" + header "git2/revwalk.h" + header "git2/signature.h" + header "git2/stash.h" + header "git2/status.h" + header "git2/submodule.h" + header "git2/tag.h" + header "git2/transport.h" + header "git2/tree.h" + header "git2/types.h" + header "git2/version.h" + header "git2/sys/commit.h" + header "git2/sys/config.h" + header "git2/sys/diff.h" + header "git2/sys/filter.h" + header "git2/sys/hashsig.h" + header "git2/sys/index.h" + header "git2/sys/mempack.h" + header "git2/sys/merge.h" + header "git2/sys/odb_backend.h" + header "git2/sys/refdb_backend.h" + header "git2/sys/reflog.h" + header "git2/sys/refs.h" + header "git2/sys/repository.h" + header "git2/sys/transport.h" + header "git2/sys/time.h" + header "git2/cred_helpers.h" + header "git2/sys/openssl.h" + header "git2/sys/stream.h" + header "git2/trace.h" + + exclude header "git2/inttypes.h" + exclude header "git2/stdint.h" + exclude header "git2/sys/git2/annotated_commit.h" + exclude header "git2/sys/git2/attr.h" + exclude header "git2/sys/git2/blame.h" + exclude header "git2/sys/git2/blob.h" + exclude header "git2/sys/git2/branch.h" + exclude header "git2/sys/git2/buffer.h" + exclude header "git2/sys/git2/checkout.h" + exclude header "git2/sys/git2/cherrypick.h" + exclude header "git2/sys/git2/clone.h" + exclude header "git2/sys/git2/commit.h" + exclude header "git2/sys/git2/cred_helpers.h" + exclude header "git2/sys/git2/describe.h" + exclude header "git2/sys/git2/errors.h" + exclude header "git2/sys/git2/global.h" + exclude header "git2/sys/git2/graph.h" + exclude header "git2/sys/git2/ignore.h" + exclude header "git2/sys/git2/index.h" + exclude header "git2/sys/git2/indexer.h" + exclude header "git2/sys/git2/inttypes.h" + exclude header "git2/sys/git2/merge.h" + exclude header "git2/sys/git2/message.h" + exclude header "git2/sys/git2/notes.h" + exclude header "git2/sys/git2/object.h" + exclude header "git2/sys/git2/odb_backend.h" + exclude header "git2/sys/git2/oidarray.h" + exclude header "git2/sys/git2/pack.h" + exclude header "git2/sys/git2/patch.h" + exclude header "git2/sys/git2/pathspec.h" + exclude header "git2/sys/git2/rebase.h" + exclude header "git2/sys/git2/refdb.h" + exclude header "git2/sys/git2/reflog.h" + exclude header "git2/sys/git2/refs.h" + exclude header "git2/sys/git2/refspec.h" + exclude header "git2/sys/git2/remote.h" + exclude header "git2/sys/git2/repository.h" + exclude header "git2/sys/git2/reset.h" + exclude header "git2/sys/git2/revert.h" + exclude header "git2/sys/git2/revparse.h" + exclude header "git2/sys/git2/revwalk.h" + exclude header "git2/sys/git2/signature.h" + exclude header "git2/sys/git2/stash.h" + exclude header "git2/sys/git2/stdint.h" + exclude header "git2/sys/git2/strarray.h" + exclude header "git2/sys/git2/submodule.h" + exclude header "git2/sys/git2/tag.h" + exclude header "git2/sys/git2/trace.h" + exclude header "git2/sys/git2/transaction.h" + exclude header "git2/sys/git2/transport.h" + exclude header "git2/sys/git2/tree.h" + exclude header "git2/sys/git2/version.h" + exclude header "git2/sys/git2/sys/commit.h" + exclude header "git2/sys/git2/sys/config.h" + exclude header "git2/sys/git2/sys/diff.h" + exclude header "git2/sys/git2/sys/filter.h" + exclude header "git2/sys/git2/sys/hashsig.h" + exclude header "git2/sys/git2/sys/index.h" + exclude header "git2/sys/git2/sys/mempack.h" + exclude header "git2/sys/git2/sys/merge.h" + exclude header "git2/sys/git2/sys/odb_backend.h" + exclude header "git2/sys/git2/sys/openssl.h" + exclude header "git2/sys/git2/sys/refdb_backend.h" + exclude header "git2/sys/git2/sys/reflog.h" + exclude header "git2/sys/git2/sys/refs.h" + exclude header "git2/sys/git2/sys/repository.h" + exclude header "git2/sys/git2/sys/stream.h" + exclude header "git2/sys/git2/sys/time.h" + exclude header "git2/sys/git2/sys/transport.h" + exclude header "git2/sys/git2/sys/worktree.h" + + export * + module * { export * } +} diff --git a/ObjectiveGit/Categories/NSArray+StringArray.h b/ObjectiveGit/Categories/NSArray+StringArray.h new file mode 100644 index 000000000..026521ba2 --- /dev/null +++ b/ObjectiveGit/Categories/NSArray+StringArray.h @@ -0,0 +1,33 @@ +// +// NSArray+StringArray.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 08/08/2013. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/strarray.h" + +@interface NSArray (StringArray) + +/// Creates an `NSString` array from a `git_strarray`. +/// +/// The `git_strarray` must still be freed using `git_strarray_free` after use. +/// +/// strarray - The `git_strarray` to convert. +/// +/// Returns a new array with the contents of `strarray` converted to +/// `NSString`s. ++ (instancetype)git_arrayWithStrarray:(git_strarray)strarray; + +/// Creates and returns a `git_strarray` given an `NSArray` of `NSString`s. +/// +/// Must only be called with an array of `NSString`s, otherwise an assertion +/// will fail. +/// +/// Returns a `git_strarray` which must be freed using `git_strarray_free` after +/// use. +- (git_strarray)git_strarray; + +@end diff --git a/Classes/Categories/NSArray+StringArray.m b/ObjectiveGit/Categories/NSArray+StringArray.m similarity index 82% rename from Classes/Categories/NSArray+StringArray.m rename to ObjectiveGit/Categories/NSArray+StringArray.m index 65c3c55b9..17c456af0 100644 --- a/Classes/Categories/NSArray+StringArray.m +++ b/ObjectiveGit/Categories/NSArray+StringArray.m @@ -22,7 +22,13 @@ + (instancetype)git_arrayWithStrarray:(git_strarray)strarray { const char *cStr = strarray.strings[i]; if (cStr == NULL) continue; - strings[stringsCount++] = @(cStr); + NSUInteger length = strlen(cStr); + NSString *string = + [[NSString alloc] initWithBytes:cStr length:length encoding:NSUTF8StringEncoding] + ?: [[NSString alloc] initWithBytes:cStr length:length encoding:NSASCIIStringEncoding]; + if (string == nil) continue; + + strings[stringsCount++] = string; } @onExit { diff --git a/ObjectiveGit/Categories/NSData+Git.h b/ObjectiveGit/Categories/NSData+Git.h new file mode 100644 index 000000000..ddc2a91e4 --- /dev/null +++ b/ObjectiveGit/Categories/NSData+Git.h @@ -0,0 +1,36 @@ +// +// NSData+Git.h +// + +#import +#import "git2/buffer.h" +#import "git2/oid.h" + +@interface NSData (Git) + ++ (NSData *)git_dataWithOid:(git_oid *)oid; +- (BOOL)git_getOid:(git_oid *)oid error:(NSError **)error; + +/// Creates an NSData object that will take ownership of a libgit2 buffer. +/// +/// buffer - A buffer of data to wrap in NSData, which will be copied if +/// necessary. This method will replace the buffer's content with +/// a NULL pointer on success. This argument must not be NULL. +/// +/// Returns the wrapped data, or nil if memory allocation fails. ++ (instancetype)git_dataWithBuffer:(git_buf *)buffer; + +/// Returns a read-only libgit2 buffer that will proxy the current bytes of the +/// receiver. If the length of the receiver changes after this method, the +/// behavior of the returned buffer is undefined. +- (git_buf)git_buf; + +/// Creates a git_buf from the data and then checks if the buffer contains a NUL +/// byte. +- (BOOL)git_containsNUL; + +/// Creates a git_buf from the data and then checks if the buffer looks like it +/// contains binary data. +- (BOOL)git_isBinary; + +@end diff --git a/ObjectiveGit/Categories/NSData+Git.m b/ObjectiveGit/Categories/NSData+Git.m new file mode 100644 index 000000000..6498dfbe0 --- /dev/null +++ b/ObjectiveGit/Categories/NSData+Git.m @@ -0,0 +1,61 @@ +// +// NSData+Git.m +// + +#import "NSData+Git.h" +#import "NSError+Git.h" + +#import "git2/errors.h" + +@implementation NSData (Git) + ++ (NSData *)git_dataWithOid:(git_oid *)oid { + return [NSData dataWithBytes:oid length:sizeof(git_oid)]; +} + +- (BOOL)git_getOid:(git_oid *)oid error:(NSError **)error { + if ([self length] != sizeof(git_oid)) { + if (error != NULL) { + *error = [NSError errorWithDomain:GTGitErrorDomain + code:GIT_ERROR_INVALID + userInfo: + [NSDictionary dictionaryWithObject:@"can't extract oid from data of incorrect length" + forKey:NSLocalizedDescriptionKey]]; + } + return NO; + } + + [self getBytes:oid length:sizeof(git_oid)]; + return YES; +} + ++ (instancetype)git_dataWithBuffer:(git_buf *)buffer { + NSCParameterAssert(buffer != NULL); + + if (buffer->size == 0) return [self data]; + + // Ensure that the buffer is actually allocated dynamically, not pointing to + // some data which may disappear. + if (git_buf_grow(buffer, 0) != GIT_OK) return nil; + + NSData *data = [self dataWithBytesNoCopy:buffer->ptr length:buffer->size freeWhenDone:YES]; + *buffer = (git_buf)GIT_BUF_INIT_CONST(0, NULL); + + return data; +} + +- (git_buf)git_buf { + return (git_buf)GIT_BUF_INIT_CONST((void *)self.bytes, self.length); +} + +- (BOOL)git_containsNUL { + git_buf buffer = self.git_buf; + return git_buf_contains_nul(&buffer) > 0; +} + +- (BOOL)git_isBinary { + git_buf buffer = self.git_buf; + return git_buf_is_binary(&buffer) > 0; +} + +@end diff --git a/ObjectiveGit/Categories/NSDate+GTTimeAdditions.h b/ObjectiveGit/Categories/NSDate+GTTimeAdditions.h new file mode 100644 index 000000000..e87210310 --- /dev/null +++ b/ObjectiveGit/Categories/NSDate+GTTimeAdditions.h @@ -0,0 +1,40 @@ +// +// NSDate+GTTimeAdditions.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 27/03/2013. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/types.h" + +@interface NSDate (GTTimeAdditions) + +/// Creates a new `NSDate` from the provided `git_time`. +/// +/// time - The `git_time` to base the returned date on. +/// +/// Returns an `NSDate` object representing the passed in `time`. ++ (NSDate *)gt_dateFromGitTime:(git_time)time; + +/// Converts the date to a `git_time`. +/// +/// timeZone - An `NSTimeZone` to describe the time offset. This is optional, if +/// `nil` the default time zone will be used. +- (git_time)gt_gitTimeUsingTimeZone:(NSTimeZone *)timeZone; + +@end + +@interface NSTimeZone (GTTimeAdditions) + +/// Creates a new `NSTimeZone` from the provided `git_time`. +/// +/// Returns an `NSTimeZone` object representing the time zone for the offset in +/// the passed in `git_time`. ++ (NSTimeZone *)gt_timeZoneFromGitTime:(git_time)time; + +/// The difference, in minutes, between the current default timezone and GMT. +@property (nonatomic, readonly) int gt_gitTimeOffset; + +@end diff --git a/Classes/Categories/NSDate+GTTimeAdditions.m b/ObjectiveGit/Categories/NSDate+GTTimeAdditions.m similarity index 100% rename from Classes/Categories/NSDate+GTTimeAdditions.m rename to ObjectiveGit/Categories/NSDate+GTTimeAdditions.m diff --git a/ObjectiveGit/Categories/NSError+Git.h b/ObjectiveGit/Categories/NSError+Git.h new file mode 100644 index 000000000..4f7532f62 --- /dev/null +++ b/ObjectiveGit/Categories/NSError+Git.h @@ -0,0 +1,95 @@ +// +// NSError+Git.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 2/17/11. +// +// The MIT License +// +// Copyright (c) 2011 Tim Clem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +/// The error domain used by Objective-Git +extern NSString * const GTGitErrorDomain; + +/// Error userinfo keys +extern NSString * const GTGitErrorOID; + +@interface NSError (Git) + +/// Describes the given libgit2 error code, using any message provided by +/// libgit2 or the OS. +/// +/// code - The error code returned from libgit2. +/// +/// Returns a non-nil NSError. ++ (NSError *)git_errorFor:(int)code; + +/// Describes the given libgit2 error code, using `desc` as the error's +/// description, and a failure reason from `reason` and the arguments that +/// follow. +/// +/// The created error will also have an `NSUnderlyingErrorKey` that contains the +/// result of +git_errorFor: on the same error code. +/// +/// code - The error code returned from libgit2. +/// desc - The description to use in the created NSError. This may be nil. +/// reason - A format string to use for the created NSError's failure reason. +/// This may be nil. +/// ... - Format arguments to insert into `reason`. +/// +/// Returns a non-nil NSError. ++ (NSError *)git_errorFor:(int)code description:(NSString *)desc failureReason:(NSString *)reason, ... NS_FORMAT_FUNCTION(3, 4); + +/// Describes the given libgit2 error code, using `desc` and the arguments that +/// follow as the error's description. +/// +/// The created error will also have an `NSUnderlyingErrorKey` that contains the +/// result of +git_errorFor: on the same error code. +/// +/// code - The error code returned from libgit2. +/// desc - A format string to use for the created NSError's description. This may be nil. +/// ... - Format arguments to insert into `desc`. +/// +/// Returns a non-nil NSError. ++ (NSError *)git_errorFor:(int)code description:(NSString *)desc, ... NS_FORMAT_FUNCTION(2, 3); + + +/// Describes the given libgit2 error code, using `desc` as the error's +/// description, and a failure reason from `reason` and the arguments that +/// follow. +/// +/// The created error will also have an `NSUnderlyingErrorKey` that contains the +/// result of +git_errorFor: on the same error code. +/// +/// code - The error code returned from libgit2. +/// desc - The description to use in the created NSError. This may be nil. +/// userInfo - A dictionary of additional values to insert in the NSError userInfo. +/// This may be nil. +/// reason - A format string to use for the created NSError's failure reason. +/// This may be nil. +/// ... - Format arguments to insert into `reason`. +/// +/// Returns a non-nil NSError. ++ (NSError *)git_errorFor:(int)code description:(NSString *)desc userInfo:(NSDictionary *)userInfo failureReason:(NSString *)reason, ... NS_FORMAT_FUNCTION(4, 5); +@end diff --git a/Classes/Categories/NSError+Git.m b/ObjectiveGit/Categories/NSError+Git.m similarity index 75% rename from Classes/Categories/NSError+Git.m rename to ObjectiveGit/Categories/NSError+Git.m index 681e10034..422b32d1f 100644 --- a/Classes/Categories/NSError+Git.m +++ b/ObjectiveGit/Categories/NSError+Git.m @@ -28,9 +28,10 @@ // #import "NSError+Git.h" -#include "git2.h" +#import "git2/errors.h" NSString * const GTGitErrorDomain = @"GTGitErrorDomain"; +NSString * const GTGitErrorOID = @"GTOID"; @implementation NSError (Git) @@ -67,6 +68,26 @@ + (NSError *)git_errorFor:(int)code description:(NSString *)desc failureReason:( return [NSError errorWithDomain:GTGitErrorDomain code:code userInfo:userInfo]; } ++ (NSError *)git_errorFor:(int)code description:(NSString *)desc userInfo:(NSDictionary *)additionalUserInfo failureReason:(NSString *)reason, ... { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:additionalUserInfo]; + + if (desc != nil) userInfo[NSLocalizedDescriptionKey] = desc; + if (reason != nil) { + va_list args; + va_start(args, reason); + + NSString *formattedReason = [[NSString alloc] initWithFormat:reason arguments:args]; + va_end(args); + + userInfo[NSLocalizedFailureReasonErrorKey] = formattedReason; + } + + NSError *underError = [self git_errorFor:code]; + if (underError != nil) userInfo[NSUnderlyingErrorKey] = underError; + + return [NSError errorWithDomain:GTGitErrorDomain code:code userInfo:userInfo]; +} + + (NSError *)git_errorFor:(int)code { NSDictionary *userInfo = nil; @@ -77,10 +98,10 @@ + (NSError *)git_errorFor:(int)code { } + (NSString *)git_descriptionForErrorCode:(int)code { - const git_error *gitLastError = giterr_last(); + const git_error *gitLastError = git_error_last(); if (gitLastError != NULL) { return @(gitLastError->message); - } else if (code == GITERR_OS) { + } else if (code == GIT_ERROR_OS) { return @(strerror(errno)); } else { return nil; diff --git a/Classes/Categories/NSString+Git.h b/ObjectiveGit/Categories/NSString+Git.h similarity index 87% rename from Classes/Categories/NSString+Git.h rename to ObjectiveGit/Categories/NSString+Git.h index 171475e03..9b049b34f 100644 --- a/Classes/Categories/NSString+Git.h +++ b/ObjectiveGit/Categories/NSString+Git.h @@ -27,14 +27,14 @@ // THE SOFTWARE. // -#include "git2.h" +#import @interface NSString (Git) -// Get a short unique sha1 for a full sha1 -// -// returns a NSString of the shortened sha1 -// returns nil if the receiver is not a sha string or is too short +/// Get a short unique sha1 for a full sha1 +/// +/// returns a NSString of the shortened sha1 +/// returns nil if the receiver is not a sha string or is too short - (NSString *)git_shortUniqueShaString; @end diff --git a/Classes/Categories/NSString+Git.m b/ObjectiveGit/Categories/NSString+Git.m similarity index 100% rename from Classes/Categories/NSString+Git.m rename to ObjectiveGit/Categories/NSString+Git.m diff --git a/ObjectiveGit/GTBlame.h b/ObjectiveGit/GTBlame.h new file mode 100644 index 000000000..12db9a5c2 --- /dev/null +++ b/ObjectiveGit/GTBlame.h @@ -0,0 +1,62 @@ +// +// GTBlame.h +// ObjectiveGitFramework +// +// Created by David Catmull on 11/6/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/blame.h" + +@class GTBlameHunk; +@class GTRepository; + +NS_ASSUME_NONNULL_BEGIN + +/// A `GTBlame` provides authorship info, through `GTBlameHunk` for each line of a file. Analogous to `git_blame` in libgit2. +@interface GTBlame : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer. +/// +/// blame - A git_blame to wrap. May not be NULL. +/// +/// Returns a blame, or nil if initialization failed. +- (instancetype _Nullable)initWithGitBlame:(git_blame *)blame NS_DESIGNATED_INITIALIZER; + +/// Get all the hunks in the blame. A convenience wrapper around `enumerateHunksUsingBlock:` +@property (nonatomic, strong, readonly) NSArray *hunks; + +/// The number of hunks in the blame. +@property (nonatomic, readonly) NSUInteger hunkCount; + +/// Get the hunk at the specified index. +/// +/// index - The index to retrieve the hunk from. +/// +/// Returns a `GTBlameHunk` or nil if an error occurred. +- (GTBlameHunk * _Nullable)hunkAtIndex:(NSUInteger)index; + +/// Enumerate the hunks in the blame. +/// +/// block - A block invoked for every hunk in the blame. +/// Setting stop to `YES` instantly stops the enumeration. +/// May not be NULL. +/// +- (void)enumerateHunksUsingBlock:(void (^)(GTBlameHunk *hunk, NSUInteger index, BOOL *stop))block; + +/// Get the hunk that relates to the given line number in the newest commit. +/// +/// lineNumber - The (1 based) line number to find a hunk for. +/// +/// Returns a `GTBlameHunk` or nil if an error occurred. +- (GTBlameHunk * _Nullable)hunkAtLineNumber:(NSUInteger)lineNumber; + +/// The underlying `git_blame` object. +- (git_blame *)git_blame __attribute__((objc_returns_inner_pointer)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTBlame.m b/ObjectiveGit/GTBlame.m similarity index 95% rename from Classes/GTBlame.m rename to ObjectiveGit/GTBlame.m index c2dcb5e9a..c1dee263f 100644 --- a/Classes/GTBlame.m +++ b/ObjectiveGit/GTBlame.m @@ -21,6 +21,11 @@ @interface GTBlame () @implementation GTBlame +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitBlame:(git_blame *)blame { NSParameterAssert(blame != NULL); diff --git a/ObjectiveGit/GTBlameHunk.h b/ObjectiveGit/GTBlameHunk.h new file mode 100644 index 000000000..72e3f1764 --- /dev/null +++ b/ObjectiveGit/GTBlameHunk.h @@ -0,0 +1,51 @@ +// +// GTBlameHunk.h +// ObjectiveGitFramework +// +// Created by David Catmull on 11/6/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/blame.h" + +@class GTOID; +@class GTSignature; + +NS_ASSUME_NONNULL_BEGIN + +/// A `GTBlameHunk` is an object that provides authorship info for a set of lines in a `GTBlame`. +@interface GTBlameHunk : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer. +/// +/// hunk - A git_blame_hunk to wrap. May not be NULL. +/// +/// Returns a blame hunk, or nil if initialization failed. +- (instancetype _Nullable)initWithGitBlameHunk:(git_blame_hunk)hunk NS_DESIGNATED_INITIALIZER; + +/// A NSRange where `location` is the (1 based) starting line number, +/// and `length` is the number of lines in the hunk. +@property (nonatomic, readonly) NSRange lines; + +/// The OID of the commit where this hunk was last changed. +@property (nonatomic, readonly, copy) GTOID * _Nullable finalCommitOID; + +/// The signature of the commit where this hunk was last changed. +@property (nonatomic, readonly) GTSignature * _Nullable finalSignature; + +/// The path of the file in the original commit. +@property (nonatomic, readonly, copy) NSString *originalPath; + +/// `YES` if the blame stopped trying before the commit where the line was added was found. +/// This could happen if you use `GTBlameOptionsOldestCommitOID`. +@property (nonatomic, getter = isBoundary, readonly) BOOL boundary; + +/// The git_blame_hunk represented by the receiver. +@property (nonatomic, readonly) git_blame_hunk git_blame_hunk; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTBlameHunk.m b/ObjectiveGit/GTBlameHunk.m similarity index 88% rename from Classes/GTBlameHunk.m rename to ObjectiveGit/GTBlameHunk.m index 4fc016c99..9c136fc70 100644 --- a/Classes/GTBlameHunk.m +++ b/ObjectiveGit/GTBlameHunk.m @@ -12,6 +12,11 @@ @implementation GTBlameHunk +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitBlameHunk:(git_blame_hunk)hunk { self = [super init]; if (self == nil) return nil; @@ -34,7 +39,9 @@ - (GTSignature *)finalSignature { } - (NSString *)originalPath { - return @(self.git_blame_hunk.orig_path); + NSString *path = @(self.git_blame_hunk.orig_path); + NSAssert(path, @"string was nil"); + return path; } - (BOOL)isBoundary { diff --git a/ObjectiveGit/GTBlob.h b/ObjectiveGit/GTBlob.h new file mode 100644 index 000000000..6865ee8a6 --- /dev/null +++ b/ObjectiveGit/GTBlob.h @@ -0,0 +1,120 @@ +// +// GTBlob.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 2/25/11. +// +// The MIT License +// +// Copyright (c) 2011 Tim Clem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + + +#import "GTObject.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GTBlob : GTObject + +/// Creates a new blob from the given string. +/// +/// This writes data to the repository's object database. +/// +/// string - The string to add. This must not be nil. +/// repository - The repository to put the object in. This must not be nil. +/// error - Will be set if an error occurs. This may be nil. +/// +/// Return a newly created blob object, or nil if an error occurs. ++ (instancetype _Nullable)blobWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error; + +/// Creates a new blob from the given data. +/// +/// This writes data to the repository's object database. +/// +/// data - The data to add. This must not be nil. +/// repository - The repository to put the object in. This must not be nil. +/// error - Will be set if an error occurs. This may be nil. +/// +/// Return a newly created blob object, or nil if an error occurs. ++ (instancetype _Nullable)blobWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error; + +/// Creates a new blob given an NSURL to a file. +/// +/// This copies the data from the file to the repository's object database. +/// +/// file - The NSURL of the file to add. This must not be nil. +/// repository - The repository to put the object in. This must not be nil. +/// error - Will be set if an error occurs. This may be nil. +/// +/// Return a newly created blob object, or nil if an error occurs. ++ (instancetype _Nullable)blobWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error; + +/// Creates a new blob from the given string. +/// +/// Convenience wrapper around `-initWithData:inRepository:error` that converts the string to UTF8 data +/// +/// string - The string to add. This must not be nil. +/// repository - The repository to put the object in. This must not be nil. +/// error - Will be set if an error occurs. This may be nil. +/// +/// Return a newly created blob object, or nil if an error occurs. +- (instancetype _Nullable)initWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error; + +/// Creates a new blob from the passed data. +/// +/// This writes data to the repository's object database. +/// +/// data - The data to write. This must not be nil. +/// repository - The repository to put the object in. This must not be nil. +/// error - Will be set if an error occurs. This may be nil. +/// +/// Returns a newly created blob object, or nil if an error occurs. +- (instancetype _Nullable)initWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error; + +/// Creates a new blob from the specified file. +/// +/// This copies the data from the file to the repository's object database. +/// +/// file - The file to copy contents from. This must not be nil. +/// repository - The repository to put the object in. This must not be nil. +/// error - Will be set if an error occurs. This may be nil. +/// +/// Returns a newly created blob object, or nil if an error occurs. +- (instancetype _Nullable)initWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error; + +/// The underlying `git_object` as a `git_blob` object. +- (git_blob *)git_blob __attribute__((objc_returns_inner_pointer)); + +- (git_off_t)size; +- (NSString * _Nullable)content; +- (NSData *)data; + +/// Attempts to apply the filter list for `path` to the blob. +/// +/// path - The path to use filters from. Must not be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the filtered data, or nil if an error occurs. +- (NSData * _Nullable)applyFiltersForPath:(NSString *)path error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTBlob.m b/ObjectiveGit/GTBlob.m similarity index 69% rename from Classes/GTBlob.m rename to ObjectiveGit/GTBlob.m index c522e8be8..a1634dcf5 100644 --- a/Classes/GTBlob.m +++ b/ObjectiveGit/GTBlob.m @@ -28,38 +28,42 @@ // #import "GTBlob.h" -#import "NSError+Git.h" + #import "GTRepository.h" +#import "NSData+Git.h" +#import "NSError+Git.h" #import "NSString+Git.h" +#import "git2/blob.h" +#import "git2/errors.h" @implementation GTBlob - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p> size: %zi, content: %@, data = %@", NSStringFromClass([self class]), self, [self size], [self content], [self data]]; + return [NSString stringWithFormat:@"<%@: %p> size: %lli, content: %@, data = %@", NSStringFromClass([self class]), self, [self size], [self content], [self data]]; } #pragma mark API -+ (id)blobWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error { ++ (instancetype)blobWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error { return [[self alloc] initWithString:string inRepository:repository error:error]; } -+ (id)blobWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error { ++ (instancetype)blobWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error { return [[self alloc] initWithData:data inRepository:repository error:error]; } -+ (id)blobWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error { ++ (instancetype)blobWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error { return [[self alloc] initWithFile:file inRepository:repository error:error]; } -- (id)initWithOid:(const git_oid *)oid inRepository:(GTRepository *)repository error:(NSError **)error { +- (instancetype)initWithOid:(const git_oid *)oid inRepository:(GTRepository *)repository error:(NSError **)error { NSParameterAssert(oid != NULL); NSParameterAssert(repository != nil); git_object *obj; - int gitError = git_object_lookup(&obj, repository.git_repository, oid, (git_otype) GTObjectTypeBlob); + int gitError = git_object_lookup(&obj, repository.git_repository, oid, (git_object_t) GTObjectTypeBlob); if (gitError < GIT_OK) { if (error != NULL) { *error = [NSError git_errorFor:gitError description:@"Failed to lookup blob"]; @@ -70,12 +74,12 @@ - (id)initWithOid:(const git_oid *)oid inRepository:(GTRepository *)repository e return [self initWithObj:obj inRepository:repository]; } -- (id)initWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error { +- (instancetype)initWithString:(NSString *)string inRepository:(GTRepository *)repository error:(NSError **)error { NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; return [self initWithData:data inRepository:repository error:error]; } -- (id)initWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error { +- (instancetype)initWithData:(NSData *)data inRepository:(GTRepository *)repository error:(NSError **)error { NSParameterAssert(data != nil); NSParameterAssert(repository != nil); @@ -91,7 +95,7 @@ - (id)initWithData:(NSData *)data inRepository:(GTRepository *)repository error: return [self initWithOid:&oid inRepository:repository error:error]; } -- (id)initWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error { +- (instancetype)initWithFile:(NSURL *)file inRepository:(GTRepository *)repository error:(NSError **)error { NSParameterAssert(file != nil); NSParameterAssert(repository != nil); @@ -129,4 +133,17 @@ - (NSData *)data { return [NSData dataWithBytes:git_blob_rawcontent(self.git_blob) length:(NSUInteger)s]; } +- (NSData *)applyFiltersForPath:(NSString *)path error:(NSError **)error { + NSCParameterAssert(path != nil); + + git_buf buffer = GIT_BUF_INIT_CONST(0, NULL); + int gitError = git_blob_filtered_content(&buffer, self.git_blob, path.UTF8String, 1); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to apply filters for path %@ to blob", path]; + return nil; + } + + return [NSData git_dataWithBuffer:&buffer]; +} + @end diff --git a/ObjectiveGit/GTBranch.h b/ObjectiveGit/GTBranch.h new file mode 100644 index 000000000..3b3be239d --- /dev/null +++ b/ObjectiveGit/GTBranch.h @@ -0,0 +1,139 @@ +// +// GTBranch.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 3/3/11. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "GTObject.h" + +@class GTCommit; +@class GTReference; +@class GTRemote; +@class GTRepository; + +typedef NS_ENUM(NSInteger, GTBranchType) { + GTBranchTypeLocal = GIT_BRANCH_LOCAL, + GTBranchTypeRemote = GIT_BRANCH_REMOTE, +}; + +NS_ASSUME_NONNULL_BEGIN + +/// A git branch object. +/// +/// Branches are considered to be equivalent if both their `name` and `SHA` are +/// equal. +@interface GTBranch : NSObject + +@property (nonatomic, readonly) NSString * _Nullable name; +@property (nonatomic, readonly) NSString * _Nullable shortName; +@property (nonatomic, copy, readonly) GTOID * _Nullable OID; +@property (nonatomic, readonly) NSString * _Nullable remoteName; +@property (nonatomic, readonly) GTBranchType branchType; +@property (nonatomic, readonly, strong) GTRepository *repository; +@property (nonatomic, readonly, strong) GTReference *reference; +@property (nonatomic, readonly, getter=isHEAD) BOOL HEAD; + ++ (NSString *)localNamePrefix; ++ (NSString *)remoteNamePrefix; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer. +/// +/// ref - The branch reference to wrap. Must not be nil. +/// +/// Returns the initialized receiver. +- (instancetype _Nullable)initWithReference:(GTReference *)ref NS_DESIGNATED_INITIALIZER; + +/// Convenience class initializer. +/// +/// ref - The branch reference to wrap. Must not be nil. +/// +/// Returns an initialized instance. ++ (instancetype _Nullable)branchWithReference:(GTReference *)ref; + +/// Get the target commit for this branch +/// +/// error(out) - will be filled if an error occurs +/// +/// returns a GTCommit object or nil if an error occurred +- (GTCommit * _Nullable)targetCommitWithError:(NSError **)error; + +/// Renames the branch. Setting `force` to YES to delete another branch with the same name. +- (BOOL)rename:(NSString *)name force:(BOOL)force error:(NSError **)error; + +/// Count all commits in this branch +/// +/// error(out) - will be filled if an error occurs +/// +/// returns number of commits in the branch or NSNotFound if an error occurred +- (NSUInteger)numberOfCommitsWithError:(NSError **)error; + +/// Get unique commits +/// +/// otherBranch - +/// error - If not NULL, set to any error that occurs. +/// +/// Returns a (possibly empty) array of GTCommits, or nil if an error occurs. +- (NSArray * _Nullable)uniqueCommitsRelativeToBranch:(GTBranch *)otherBranch error:(NSError **)error; + +/// Deletes the local branch and nils out the reference. +- (BOOL)deleteWithError:(NSError **)error; + +/// If the receiver is a local branch, looks up and returns its tracking branch. +/// If the receiver is a remote branch, returns self. If no tracking branch was +/// found, returns nil and sets `success` to YES. +- (GTBranch * _Nullable)trackingBranchWithError:(NSError **)error success:(BOOL * _Nullable)success; + +/// Update the tracking branch. +/// +/// trackingBranch - The tracking branch for the receiver. If nil, it unsets the +/// tracking branch. +/// error - The error if one occurred. +/// +/// Returns whether it was successful. +- (BOOL)updateTrackingBranch:(GTBranch * _Nullable)trackingBranch error:(NSError **)error; + +/// Reloads the branch's reference and creates a new branch based off that newly +/// loaded reference. +/// +/// This does *not* change the receiver. +/// +/// error - The error if one occurred. +/// +/// Returns the reloaded branch, or nil if an error occurred. +- (GTBranch * _Nullable)reloadedBranchWithError:(NSError **)error; + +/// Calculate the ahead/behind count from this branch to the given branch. +/// +/// ahead - The number of commits which are unique to the receiver. Cannot be +/// NULL. +/// behind - The number of commits which are unique to `branch`. Cannot be NULL. +/// branch - The branch to which the receiver should be compared. +/// error - The error if one occurs. +/// +/// Returns whether the calculation was successful. +- (BOOL)calculateAhead:(size_t *)ahead behind:(size_t *)behind relativeTo:(GTBranch *)branch error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTBranch.m b/ObjectiveGit/GTBranch.m similarity index 54% rename from Classes/GTBranch.m rename to ObjectiveGit/GTBranch.m index 764cbdfca..ec54cbd5d 100644 --- a/Classes/GTBranch.m +++ b/ObjectiveGit/GTBranch.m @@ -24,27 +24,35 @@ // #import "GTBranch.h" -#import "GTReference.h" + +#import "GTCommit.h" #import "GTEnumerator.h" +#import "GTOID.h" +#import "GTReference.h" +#import "GTRemote.h" #import "GTRepository.h" -#import "GTCommit.h" #import "NSError+Git.h" +#import "NSData+Git.h" + +#import "git2/branch.h" +#import "git2/errors.h" +#import "git2/graph.h" @implementation GTBranch - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p> name: %@, shortName: %@, sha: %@, remoteName: %@, repository: %@", NSStringFromClass([self class]), self, self.name, self.shortName, self.SHA, self.remoteName, self.repository]; + return [NSString stringWithFormat:@"<%@: %p> name: %@, shortName: %@, sha: %@, remoteName: %@, repository: %@", NSStringFromClass([self class]), self, self.name, self.shortName, self.OID, self.remoteName, self.repository]; } - (BOOL)isEqual:(GTBranch *)otherBranch { if (otherBranch == self) return YES; if (![otherBranch isKindOfClass:self.class]) return NO; - return [self.name isEqual:otherBranch.name] && [self.SHA isEqual:otherBranch.SHA]; + return [self.name isEqual:otherBranch.name] && [self.OID isEqual:otherBranch.OID]; } - (NSUInteger)hash { - return self.name.hash ^ self.SHA.hash; + return self.name.hash ^ self.OID.hash; } @@ -58,25 +66,32 @@ + (NSString *)remoteNamePrefix { return @"refs/remotes/"; } -+ (id)branchWithReference:(GTReference *)ref repository:(GTRepository *)repo { - return [[self alloc] initWithReference:ref repository:repo]; ++ (instancetype)branchWithReference:(GTReference *)ref { + return [[self alloc] initWithReference:ref]; } -- (id)initWithReference:(GTReference *)ref repository:(GTRepository *)repo { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithReference:(GTReference *)ref { NSParameterAssert(ref != nil); - NSParameterAssert(repo != nil); self = [super init]; if (self == nil) return nil; - _repository = repo; _reference = ref; return self; } - (NSString *)name { - return self.reference.name; + const char *charName; + int gitError = git_branch_name(&charName, self.reference.git_reference); + if (gitError != GIT_OK || charName == NULL) return nil; + + return @(charName); } - (NSString *)shortName { @@ -95,41 +110,44 @@ - (NSString *)shortName { return @(name); } -- (NSString *)SHA { - return self.reference.targetSHA; +- (GTOID *)OID { + return self.reference.targetOID; } - (NSString *)remoteName { - if (self.branchType == GTBranchTypeLocal) return nil; - - const char *name; - int gitError = git_branch_name(&name, self.reference.git_reference); + git_buf remote_name = GIT_BUF_INIT_CONST(0, NULL); + int gitError = git_branch_remote_name(&remote_name, self.repository.git_repository, self.reference.name.UTF8String); if (gitError != GIT_OK) return nil; - // Find out where the remote name ends. - const char *end = strchr(name, '/'); - if (end == NULL || end == name) return nil; - - return [[NSString alloc] initWithBytes:name length:end - name encoding:NSUTF8StringEncoding]; + NSData *data = [NSData git_dataWithBuffer:&remote_name]; + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } -- (GTCommit *)targetCommitAndReturnError:(NSError **)error { - if (self.SHA == nil) { +- (GTCommit *)targetCommitWithError:(NSError **)error { + GTOID *oid = self.OID; + if (oid == nil) { if (error != NULL) *error = GTReference.invalidReferenceError; return nil; } - return [self.repository lookUpObjectBySHA:self.SHA objectType:GTObjectTypeCommit error:error]; + return [self.repository lookUpObjectByOID:oid objectType:GTObjectTypeCommit error:error]; } - (NSUInteger)numberOfCommitsWithError:(NSError **)error { GTEnumerator *enumerator = [[GTEnumerator alloc] initWithRepository:self.repository error:error]; if (enumerator == nil) return NSNotFound; - if (![enumerator pushSHA:self.SHA error:error]) return NSNotFound; + GTOID *oid = self.OID; + if (oid == nil) return NSNotFound; + + if (![enumerator pushSHA:oid.SHA error:error]) return NSNotFound; return [enumerator countRemainingObjects:error]; } +- (GTRepository *)repository { + return self.reference.repository; +} + - (GTBranchType)branchType { if (self.reference.remote) { return GTBranchTypeRemote; @@ -138,23 +156,14 @@ - (GTBranchType)branchType { } } -- (NSArray *)uniqueCommitsRelativeToBranch:(GTBranch *)otherBranch error:(NSError **)error { - NSParameterAssert(otherBranch != nil); - - GTCommit *mergeBase = [self.repository mergeBaseBetweenFirstOID:self.reference.OID secondOID:otherBranch.reference.OID error:error]; - if (mergeBase == nil) return nil; - - GTEnumerator *enumerator = [[GTEnumerator alloc] initWithRepository:self.repository error:error]; - if (enumerator == nil) return nil; - - [enumerator resetWithOptions:GTEnumeratorOptionsTimeSort]; - - BOOL success = [enumerator pushSHA:self.SHA error:error]; - if (!success) return nil; - - success = [enumerator hideSHA:mergeBase.SHA error:error]; - if (!success) return nil; +- (BOOL)isHEAD { + return (git_branch_is_head(self.reference.git_reference) ? YES : NO); +} +- (NSArray *)uniqueCommitsRelativeToBranch:(GTBranch *)otherBranch error:(NSError **)error { + GTOID *oid = self.OID; + GTOID *otherOID = otherBranch.OID; + GTEnumerator *enumerator = [self.repository enumeratorForUniqueCommitsFromOID:oid relativeToOID:otherOID error:error]; return [enumerator allObjectsWithError:error]; } @@ -168,9 +177,29 @@ - (BOOL)deleteWithError:(NSError **)error { return YES; } +- (BOOL)rename:(NSString *)name force:(BOOL)force error:(NSError **)error { + git_reference *git_ref; + int gitError = git_branch_move(&git_ref, self.reference.git_reference, name.UTF8String, (force ? 1 : 0)); + if (gitError != GIT_OK) { + if (error) *error = [NSError git_errorFor:gitError description:@"Rename branch failed"]; + return NO; + } + + GTReference *renamedRef = [[GTReference alloc] initWithGitReference:git_ref repository:self.repository]; + NSAssert(renamedRef, @"Unable to allocate renamed ref"); + _reference = renamedRef; + + return YES; +} + - (GTBranch *)trackingBranchWithError:(NSError **)error success:(BOOL *)success { + BOOL underSuccess = NO; + if (success == NULL) { + success = &underSuccess; + } + if (self.branchType == GTBranchTypeRemote) { - if (success != NULL) *success = YES; + *success = YES; return self; } @@ -179,48 +208,60 @@ - (GTBranch *)trackingBranchWithError:(NSError **)error success:(BOOL *)success // GIT_ENOTFOUND means no tracking branch found. if (gitError == GIT_ENOTFOUND) { - if (success != NULL) *success = YES; + *success = YES; return nil; } if (gitError != GIT_OK) { - if (success != NULL) *success = NO; + *success = NO; if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create reference to tracking branch from %@", self]; return nil; } if (trackingRef == NULL) { - if (success != NULL) *success = NO; + *success = NO; if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Got a NULL remote ref for %@", self]; return nil; } - if (success != NULL) *success = YES; + GTReference *upsteamRef = [[GTReference alloc] initWithGitReference:trackingRef repository:self.repository]; + if (upsteamRef == nil) { + *success = NO; + if (error != NULL) *error = [NSError git_errorFor:GIT_ERROR description:@"Failed to allocate upstream ref"]; + return nil; + } + + *success = YES; + + return [[self class] branchWithReference:upsteamRef]; +} + +- (BOOL)updateTrackingBranch:(GTBranch *)trackingBranch error:(NSError **)error { + int result = GIT_ENOTFOUND; + if (trackingBranch.branchType == GTBranchTypeRemote) { + result = git_branch_set_upstream(self.reference.git_reference, [trackingBranch.name stringByReplacingOccurrencesOfString:[GTBranch remoteNamePrefix] withString:@""].UTF8String); + } else { + result = git_branch_set_upstream(self.reference.git_reference, trackingBranch.shortName.UTF8String); + } + if (result != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:result description:@"Failed to update tracking branch for %@", self]; + return NO; + } - return [[self class] branchWithReference:[[GTReference alloc] initWithGitReference:trackingRef repository:self.repository] repository:self.repository]; + return YES; } - (GTBranch *)reloadedBranchWithError:(NSError **)error { GTReference *reloadedRef = [self.reference reloadedReferenceWithError:error]; if (reloadedRef == nil) return nil; - return [[self.class alloc] initWithReference:reloadedRef repository:self.repository]; + return [[self.class alloc] initWithReference:reloadedRef]; } - (BOOL)calculateAhead:(size_t *)ahead behind:(size_t *)behind relativeTo:(GTBranch *)branch error:(NSError **)error { - if (branch == nil) { - *ahead = 0; - *behind = 0; - return YES; - } - - int errorCode = git_graph_ahead_behind(ahead, behind, self.repository.git_repository, branch.reference.git_oid, self.reference.git_oid); - if (errorCode != GIT_OK && error != NULL) { - *error = [NSError git_errorFor:errorCode description:@"Failed to calculate ahead/behind count of %@ relative to %@", self, branch]; - return NO; - } - - return YES; + GTOID *oid = self.OID; + GTOID *branchOID = branch.OID; + return [self.repository calculateAhead:ahead behind:behind ofOID:oid relativeToOID:branchOID error:error]; } @end diff --git a/ObjectiveGit/GTCheckoutOptions.h b/ObjectiveGit/GTCheckoutOptions.h new file mode 100644 index 000000000..f8e6277d9 --- /dev/null +++ b/ObjectiveGit/GTCheckoutOptions.h @@ -0,0 +1,103 @@ +// +// GTCheckoutOptions.h +// ObjectiveGitFramework +// +// Created by Etienne on 10/04/2015. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/checkout.h" + +@class GTDiffFile; + +NS_ASSUME_NONNULL_BEGIN + +/// Checkout strategies used by the various -checkout... methods +/// See git_checkout_strategy_t +typedef NS_OPTIONS(NSInteger, GTCheckoutStrategyType) { + GTCheckoutStrategyNone = GIT_CHECKOUT_NONE, + GTCheckoutStrategySafe = GIT_CHECKOUT_SAFE, + GTCheckoutStrategyForce = GIT_CHECKOUT_FORCE, + GTCheckoutStrategyRecreateMissing = GIT_CHECKOUT_RECREATE_MISSING, + GTCheckoutStrategyAllowConflicts = GIT_CHECKOUT_ALLOW_CONFLICTS, + GTCheckoutStrategyRemoveUntracked = GIT_CHECKOUT_REMOVE_UNTRACKED, + GTCheckoutStrategyRemoveIgnored = GIT_CHECKOUT_REMOVE_IGNORED, + GTCheckoutStrategyUpdateOnly = GIT_CHECKOUT_UPDATE_ONLY, + GTCheckoutStrategyDontUpdateIndex = GIT_CHECKOUT_DONT_UPDATE_INDEX, + GTCheckoutStrategyNoRefresh = GIT_CHECKOUT_NO_REFRESH, + GTCheckoutStrategySkipUnmerged = GIT_CHECKOUT_SKIP_UNMERGED, + GTCheckoutStrategyUseOurs = GIT_CHECKOUT_USE_OURS, + GTCheckoutStrategyUseTheirs = GIT_CHECKOUT_USE_THEIRS, + GTCheckoutStrategyDisablePathspecMatch = GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH, + GTCheckoutStrategySkipLockedDirectories = GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES, + GTCheckoutStrategyDoNotOverwriteIgnored = GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, + GTCheckoutStrategyConflictStyleMerge = GIT_CHECKOUT_CONFLICT_STYLE_MERGE, + GTCheckoutStrategyCoflictStyleDiff3 = GIT_CHECKOUT_CONFLICT_STYLE_DIFF3, + GTCheckoutStrategyDoNotRemoveExisting = GIT_CHECKOUT_DONT_REMOVE_EXISTING, + GTCheckoutStrategyDoNotWriteIndex = GIT_CHECKOUT_DONT_WRITE_INDEX, +}; + +/// Checkout notification flags used by the various -checkout... methods +/// See git_checkout_notify_t +typedef NS_OPTIONS(NSInteger, GTCheckoutNotifyFlags) { + GTCheckoutNotifyNone = GIT_CHECKOUT_NOTIFY_NONE, + GTCheckoutNotifyConflict = GIT_CHECKOUT_NOTIFY_CONFLICT, + GTCheckoutNotifyDirty = GIT_CHECKOUT_NOTIFY_DIRTY, + GTCheckoutNotifyUpdated = GIT_CHECKOUT_NOTIFY_UPDATED, + GTCheckoutNotifyUntracked = GIT_CHECKOUT_NOTIFY_UNTRACKED, + GTCheckoutNotifyIgnored = GIT_CHECKOUT_NOTIFY_IGNORED, + + GTCheckoutNotifyAll = GIT_CHECKOUT_NOTIFY_ALL, +}; + +@interface GTCheckoutOptions : NSObject + +/// Create a checkout options object. +/// +/// Since there are many places where we can checkout data, this object allow us +/// to centralize all the various behaviors that checkout allow. +/// +/// @param strategy The checkout strategy to use. +/// @param notifyFlags The checkout events that will be notified via `notifyBlock`. +/// @param progressBlock A block that will be called for each checkout step. +/// @param notifyBlock A block that will be called for each event, @see `notifyFlags`. +/// +/// @return A newly-initialized GTCheckoutOptions object. ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags progressBlock:(void (^ _Nullable)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))progressBlock notifyBlock:(int (^ _Nullable)(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir))notifyBlock; + +/// Create a checkout options object. +/// @see +checkoutOptionsWithStrategy:notifyFlags:progressBlock:notifyBlock: ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags notifyBlock:(int (^)(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir))notifyBlock; + +/// Create a checkout options object. +/// @see +checkoutOptionsWithStrategy:notifyFlags:progressBlock:notifyBlock: ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy progressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))progressBlock; + +/// Create a checkout options object. +/// @see +checkoutOptionsWithStrategy:notifyFlags:progressBlock:notifyBlock: ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy; + +/// Get the underlying git_checkout_options struct. +/// +/// @return <#return value description#> +- (git_checkout_options *)git_checkoutOptions NS_RETURNS_INNER_POINTER; + +/// The checkout strategy to use. +@property (assign) GTCheckoutStrategyType strategy; + +/// The checkout progress block that was passed in. +@property (copy, nullable) void (^progressBlock)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps); + +/// The notification flags currently enabled. +@property (assign) GTCheckoutNotifyFlags notifyFlags; + +/// The checkout notification block that was passed in. +@property (copy, nullable) int (^notifyBlock)(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir); + +/// An array of strings used to restrict what will be checked out. +@property (copy) NSArray *pathSpecs; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTCheckoutOptions.m b/ObjectiveGit/GTCheckoutOptions.m new file mode 100644 index 000000000..354e330c6 --- /dev/null +++ b/ObjectiveGit/GTCheckoutOptions.m @@ -0,0 +1,100 @@ +// +// GTCheckoutOptions.m +// ObjectiveGitFramework +// +// Created by Etienne on 10/04/2015. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +#import "GTCheckoutOptions.h" +#import "GTDiffFile.h" +#import "NSError+Git.h" +#import "NSArray+StringArray.h" +#import "git2.h" + +// The type of block set in progressBlock for progress reporting +typedef void (^GTCheckoutProgressBlock)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps); + +// The type of block set in notifyBlock for notification reporting +typedef int (^GTCheckoutNotifyBlock)(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile * _Nullable baseline, GTDiffFile * _Nullable target, GTDiffFile * _Nullable workdir); + + +@interface GTCheckoutOptions () { + git_checkout_options _git_checkoutOptions; +} +@end + +@implementation GTCheckoutOptions + ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags progressBlock:(GTCheckoutProgressBlock _Nullable)progressBlock notifyBlock:( GTCheckoutNotifyBlock _Nullable)notifyBlock { + GTCheckoutOptions *options = [self checkoutOptionsWithStrategy:strategy]; + options.notifyFlags = notifyFlags; + options.notifyBlock = notifyBlock; + options.progressBlock = progressBlock; + return options; +} + ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy progressBlock:(GTCheckoutProgressBlock)progressBlock { + NSParameterAssert(progressBlock != nil); + GTCheckoutOptions *options = [self checkoutOptionsWithStrategy:strategy]; + options.progressBlock = progressBlock; + return options; +} + ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags notifyBlock:(GTCheckoutNotifyBlock)notifyBlock { + NSParameterAssert(notifyBlock != nil); + return [self checkoutOptionsWithStrategy:strategy notifyFlags:notifyFlags progressBlock:nil notifyBlock:notifyBlock]; +} + ++ (instancetype)checkoutOptionsWithStrategy:(GTCheckoutStrategyType)strategy { + GTCheckoutOptions *options = [[self alloc] init]; + options.strategy = strategy; + return options; +} + +- (instancetype)init { + self = [super init]; + if (self == nil) return nil; + + _git_checkoutOptions.version = GIT_CHECKOUT_OPTIONS_VERSION; + + return self; +} + +static void GTCheckoutProgressCallback(const char *path, size_t completedSteps, size_t totalSteps, void *payload) { + if (payload == NULL) return; + void (^block)(NSString *, NSUInteger, NSUInteger) = (__bridge id)payload; + NSString *nsPath = (path != NULL ? @(path) : nil); + block(nsPath, completedSteps, totalSteps); +} + +static int GTCheckoutNotifyCallback(git_checkout_notify_t why, const char *path, const git_diff_file *baseline, const git_diff_file *target, const git_diff_file *workdir, void *payload) { + if (payload == NULL) return 0; + GTCheckoutNotifyBlock block = (__bridge id)payload; + NSString *nsPath = (path != NULL ? @(path) : nil); + GTDiffFile *gtBaseline = (baseline != NULL ? [[GTDiffFile alloc] initWithGitDiffFile:*baseline] : nil); + GTDiffFile *gtTarget = (target != NULL ? [[GTDiffFile alloc] initWithGitDiffFile:*target] : nil); + GTDiffFile *gtWorkdir = (workdir != NULL ? [[GTDiffFile alloc] initWithGitDiffFile:*workdir] : nil); + return block((GTCheckoutNotifyFlags)why, nsPath, gtBaseline, gtTarget, gtWorkdir); +} + +- (git_checkout_options *)git_checkoutOptions { + _git_checkoutOptions.checkout_strategy = self.strategy; + + if (self.progressBlock != nil) { + _git_checkoutOptions.progress_cb = GTCheckoutProgressCallback; + _git_checkoutOptions.progress_payload = (__bridge void *)self.progressBlock; + } + + if (self.notifyBlock != nil) { + _git_checkoutOptions.notify_cb = GTCheckoutNotifyCallback; + _git_checkoutOptions.notify_flags = self.notifyFlags; + _git_checkoutOptions.notify_payload = (__bridge void *)self.notifyBlock; + } + + _git_checkoutOptions.paths = self.pathSpecs.git_strarray; + + return &_git_checkoutOptions; +} + +@end diff --git a/Classes/GTCommit.h b/ObjectiveGit/GTCommit.h similarity index 59% rename from Classes/GTCommit.h rename to ObjectiveGit/GTCommit.h index b0874d33a..c9a666b76 100644 --- a/Classes/GTCommit.h +++ b/ObjectiveGit/GTCommit.h @@ -33,20 +33,41 @@ @class GTSignature; @class GTTree; @class GTOID; +@class GTIndex; + +NS_ASSUME_NONNULL_BEGIN @interface GTCommit : GTObject {} -@property (nonatomic, readonly, strong) GTSignature *author; -@property (nonatomic, readonly, strong) GTSignature *committer; -@property (nonatomic, readonly, copy) NSArray *parents; -@property (nonatomic, readonly) NSString *message; +@property (nonatomic, readonly, strong) GTOID *OID; +@property (nonatomic, readonly, strong) GTSignature * _Nullable author; +@property (nonatomic, readonly, strong) GTSignature * _Nullable committer; +@property (nonatomic, readonly, copy) NSArray *parents; +@property (nonatomic, readonly, copy) NSArray *parentOIDs; +@property (nonatomic, readonly) NSString * _Nullable message; @property (nonatomic, readonly) NSString *messageDetails; @property (nonatomic, readonly) NSString *messageSummary; @property (nonatomic, readonly) NSDate *commitDate; @property (nonatomic, readonly) NSTimeZone *commitTimeZone; -@property (nonatomic, readonly) GTTree *tree; +@property (nonatomic, readonly) GTTree * _Nullable tree; + +/// Is this a merge commit? +@property (nonatomic, readonly, assign, getter = isMerge) BOOL merge; -// The underlying `git_object` as a `git_commit` object. +/// The underlying `git_object` as a `git_commit` object. - (git_commit *)git_commit __attribute__((objc_returns_inner_pointer)); +/// Merges the given commit into the receiver in memory and produces the result as +/// an index. +/// +/// otherCommit - The commit with which the receiver should be merged with. Cannot be +/// nil. +/// error - The error if one occurred. +/// +/// Returns an index which represents the result of the merge, or nil if an error +/// occurred. +- (GTIndex * _Nullable)merge:(GTCommit *)otherCommit error:(NSError **)error; + @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTCommit.m b/ObjectiveGit/GTCommit.m similarity index 76% rename from Classes/GTCommit.m rename to ObjectiveGit/GTCommit.m index 8e4b8cee4..495877a22 100644 --- a/Classes/GTCommit.m +++ b/ObjectiveGit/GTCommit.m @@ -35,6 +35,11 @@ #import "NSString+Git.h" #import "NSDate+GTTimeAdditions.h" #import "GTOID.h" +#import "GTIndex.h" + +#import "git2/commit.h" +#import "git2/errors.h" +#import "git2/merge.h" @implementation GTCommit @@ -48,6 +53,10 @@ - (git_commit *)git_commit { #pragma mark API +- (GTOID *)OID { + return [GTOID oidWithGitOid:git_commit_id(self.git_commit)]; +} + - (NSString *)message { const char *s = git_commit_message(self.git_commit); if(s == NULL) return nil; @@ -101,13 +110,30 @@ - (GTTree *)tree { int gitError = git_commit_tree(&tree, self.git_commit); if (gitError < GIT_OK) { // todo: might want to return this error (and change method signature) - GTLog("Failed to get tree with error code: %d", gitError); + NSLog(@"Failed to get tree with error code: %d", gitError); return nil; } return (GTTree *)[GTObject objectWithObj:(git_object *)tree inRepository:self.repository]; } +- (BOOL)isMerge { + return git_commit_parentcount(self.git_commit) > 1; +} + +- (NSArray *)parentOIDs { + unsigned numberOfParents = git_commit_parentcount(self.git_commit); + NSMutableArray *parents = [NSMutableArray arrayWithCapacity:numberOfParents]; + + for (unsigned i = 0; i < numberOfParents; i++) { + const git_oid *parent = git_commit_parent_id(self.git_commit, i); + + [parents addObject:[GTOID oidWithGitOid:parent]]; + } + + return parents; +} + - (NSArray *)parents { unsigned numberOfParents = git_commit_parentcount(self.git_commit); NSMutableArray *parents = [NSMutableArray arrayWithCapacity:numberOfParents]; @@ -123,4 +149,20 @@ - (NSArray *)parents { return parents; } +#pragma mark Merging + +- (GTIndex *)merge:(GTCommit *)otherCommit error:(NSError **)error { + NSParameterAssert(otherCommit != nil); + + git_index *index; + + int result = git_merge_commits(&index, self.repository.git_repository, self.git_commit, otherCommit.git_commit, NULL); + if (result != GIT_OK || index == NULL) { + if (error != NULL) *error = [NSError git_errorFor:result description:@"Failed to merge commit %@ with commit %@", self.SHA, otherCommit.SHA]; + return nil; + } + + return [[GTIndex alloc] initWithGitIndex:index repository:self.repository]; +} + @end diff --git a/ObjectiveGit/GTConfiguration+Private.h b/ObjectiveGit/GTConfiguration+Private.h new file mode 100644 index 000000000..03b6f4f12 --- /dev/null +++ b/ObjectiveGit/GTConfiguration+Private.h @@ -0,0 +1,23 @@ +// +// GTConfiguration+Private.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 9/12/12. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "GTConfiguration.h" + +@class GTRepository; + +@interface GTConfiguration () + +/// Designated initializer. +/// +/// config - The libgit2 config. Cannot be NULL. +/// repository - The repository in which the config resides. May be nil. +/// +/// Returns the initialized object. +- (instancetype _Nullable)initWithGitConfig:(git_config * _Nonnull)config repository:(GTRepository * _Nullable)repository NS_DESIGNATED_INITIALIZER; + +@end diff --git a/ObjectiveGit/GTConfiguration.h b/ObjectiveGit/GTConfiguration.h new file mode 100644 index 000000000..6c4df90a4 --- /dev/null +++ b/ObjectiveGit/GTConfiguration.h @@ -0,0 +1,52 @@ +// +// GTConfiguration.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 12/30/11. +// Copyright (c) 2011 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/types.h" + +@class GTRemote; +@class GTRepository; +@class GTSignature; + +NS_ASSUME_NONNULL_BEGIN + +@interface GTConfiguration : NSObject + +@property (nonatomic, readonly, strong) GTRepository * _Nullable repository; +@property (nonatomic, readonly, copy) NSArray *configurationKeys; + +/// The GTRemotes in the config. If the configuration isn't associated with any +/// repository, this will always be nil. +@property (nonatomic, readonly, copy) NSArray * _Nullable remotes; + +- (instancetype)init NS_UNAVAILABLE; + +/// Creates and returns a configuration which includes the global, XDG, and +/// system configurations. ++ (instancetype _Nullable)defaultConfiguration; + +/// The underlying `git_config` object. +- (git_config *)git_config __attribute__((objc_returns_inner_pointer)); + +- (void)setString:(NSString *)s forKey:(NSString *)key; +- (NSString * _Nullable)stringForKey:(NSString *)key; + +- (void)setBool:(BOOL)b forKey:(NSString *)key; +- (BOOL)boolForKey:(NSString *)key; + +- (void)setInt32:(int32_t)i forKey:(NSString *)key; +- (int32_t)int32ForKey:(NSString *)key; + +- (void)setInt64:(int64_t)i forKey:(NSString *)key; +- (int64_t)int64ForKey:(NSString *)key; + +- (BOOL)deleteValueForKey:(NSString *)key error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTConfiguration.m b/ObjectiveGit/GTConfiguration.m similarity index 76% rename from Classes/GTConfiguration.m rename to ObjectiveGit/GTConfiguration.m index 569e09886..acd46cd74 100644 --- a/Classes/GTConfiguration.m +++ b/ObjectiveGit/GTConfiguration.m @@ -10,8 +10,13 @@ #import "GTConfiguration+Private.h" #import "GTRepository.h" #import "GTRemote.h" -#import "NSError+Git.h" #import "GTSignature.h" +#import "NSData+Git.h" +#import "NSError+Git.h" + +#import "git2/config.h" +#import "git2/errors.h" +#import "git2/buffer.h" @interface GTConfiguration () @property (nonatomic, readonly, assign) git_config *git_config; @@ -28,7 +33,12 @@ - (void)dealloc { } } -- (id)initWithGitConfig:(git_config *)config repository:(GTRepository *)repository { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithGitConfig:(git_config *)config repository:(GTRepository *)repository { NSParameterAssert(config != NULL); self = [super init]; @@ -55,11 +65,10 @@ - (void)setString:(NSString *)s forKey:(NSString *)key { } - (NSString *)stringForKey:(NSString *)key { - const char *string = NULL; - git_config_get_string(&string, self.git_config, key.UTF8String); - if (string == NULL) return nil; + git_buf buffer = {}; + if (git_config_get_string_buf(&buffer, self.git_config, key.UTF8String) != 0) return nil; - return [NSString stringWithUTF8String:string]; + return [[NSString alloc] initWithData:[NSData git_dataWithBuffer:&buffer] encoding:NSUTF8StringEncoding]; } - (void)setBool:(BOOL)b forKey:(NSString *)key { @@ -103,7 +112,10 @@ - (BOOL)deleteValueForKey:(NSString *)key error:(NSError **)error { static int configCallback(const git_config_entry *entry, void *payload) { NSMutableArray *configurationKeysArray = (__bridge NSMutableArray *)payload; - [configurationKeysArray addObject:@(entry->name)]; + NSString *name = @(entry->name); + NSCAssert(name, @"string was nil"); + + [configurationKeysArray addObject:name]; return 0; } @@ -125,10 +137,12 @@ - (NSArray *)remotes { NSMutableArray *remotes = [NSMutableArray arrayWithCapacity:names.count]; for (size_t i = 0; i < names.count; i++) { const char *name = names.strings[i]; - git_remote *remote = NULL; + git_remote *git_remote = NULL; - if (git_remote_load(&remote, repository.git_repository, name) == 0) { - [remotes addObject:[[GTRemote alloc] initWithGitRemote:remote]]; + if (git_remote_lookup(&git_remote, repository.git_repository, name) == 0) { + GTRemote *remote = [[GTRemote alloc] initWithGitRemote:git_remote inRepository:repository]; + if (remote) + [remotes addObject:remote]; } } @@ -137,17 +151,4 @@ - (NSArray *)remotes { return remotes; } -#pragma mark Refresh - -- (BOOL)refresh:(NSError **)error { - int success = git_config_refresh(self.git_config); - if (success != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:success description:@"Couldn't reload the configuration from disk."]; - - return NO; - } - - return YES; -} - @end diff --git a/Classes/GTCredential+Private.h b/ObjectiveGit/GTCredential+Private.h similarity index 100% rename from Classes/GTCredential+Private.h rename to ObjectiveGit/GTCredential+Private.h diff --git a/ObjectiveGit/GTCredential.h b/ObjectiveGit/GTCredential.h new file mode 100644 index 000000000..560a8ba18 --- /dev/null +++ b/ObjectiveGit/GTCredential.h @@ -0,0 +1,94 @@ +// +// GTCredential.h +// ObjectiveGitFramework +// +// Created by Etienne on 10/09/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/transport.h" + +/// An enum describing the data needed for authentication. +/// See `git_credtype_t`. +typedef NS_ENUM(NSInteger, GTCredentialType) { + GTCredentialTypeUserPassPlaintext = GIT_CREDTYPE_USERPASS_PLAINTEXT, + GTCredentialTypeSSHKey = GIT_CREDTYPE_SSH_KEY, + GTCredentialTypeSSHCustom = GIT_CREDTYPE_SSH_CUSTOM, +}; + +NS_ASSUME_NONNULL_BEGIN + +@class GTCredential; + +/// The GTCredentialProvider acts as a proxy for GTCredential requests. +/// +/// The default implementation is used through `+providerWithBlock:`, +/// passing your own block that will build a GTCredential object. +/// But you're allowed to subclass it and handle more complex workflows. +@interface GTCredentialProvider : NSObject + +/// Creates a provider from a block. +/// +/// credentialBlock - a block that will be called when credentials are requested. +/// Must not be nil. ++ (instancetype)providerWithBlock:(GTCredential * _Nullable(^)(GTCredentialType type, NSString *URL, NSString *userName))credentialBlock; + +/// Default credential provider method. +/// +/// This method will get called when an operation requests credentials from the +/// provider. +/// +/// The default implementation calls through the `providedBlock` passed +/// in `providerWithBlock:` above, but your subclass is expected to override it +/// to do its specific work. +/// +/// type - the credential types allowed by the operation. +/// URL - the URL the operation is authenticating against. +/// userName - the user name provided by the operation. Can be nil, and might be ignored. +- (GTCredential * _Nullable)credentialForType:(GTCredentialType)type URL:(NSString * _Nullable)URL userName:(NSString * _Nullable)userName; +@end + +/// The GTCredential class is used to provide authentication data. +/// It acts as a wrapper around `git_cred` objects. +@interface GTCredential : NSObject + +/// Create a credential object from a username/password pair. +/// +/// userName - The username to authenticate as. +/// password - The password belonging to that user. +/// error - If not NULL, set to any errors that occur. +/// +/// Return a new GTCredential instance, or nil if an error occurred ++ (instancetype _Nullable)credentialWithUserName:(NSString *)userName password:(NSString *)password error:(NSError **)error; + +/// Create a credential object from a SSH keyfile +/// +/// userName - The username to authenticate as. Must not be nil. +/// publicKeyURL - The URL to the public key for that user. +/// Can be omitted to reconstruct the public key from the private key. +/// privateKeyURL - The URL to the private key for that user. Must not be nil. +/// passphrase - The passPhrase for the private key. Optional if the private key has no password. +/// error - If not NULL, set to any errors that occur. +/// +/// Return a new GTCredential instance, or nil if an error occurred ++ (instancetype _Nullable)credentialWithUserName:(NSString *)userName publicKeyURL:(NSURL * _Nullable)publicKeyURL privateKeyURL:(NSURL *)privateKeyURL passphrase:(NSString * _Nullable)passphrase error:(NSError **)error; + +/// Create a credential object from a SSH keyfile data string +/// +/// userName - The username to authenticate as. +/// publicKeyString - The string containing the public key for that user. +/// Can be omitted to reconstruct the public key from the private key. +/// privateKeyString - The URL to the private key for that user. +/// passphrase - The passPhrase for the private key. Optional if the private key has no password. +/// error - If not NULL, set to any errors that occur. +/// +/// Return a new GTCredential instance, or nil if an error occurred ++ (instancetype _Nullable)credentialWithUserName:(NSString *)userName publicKeyString:(NSString * _Nullable)publicKeyString privateKeyString:(NSString *)privateKeyString passphrase:(NSString * _Nullable)passphrase error:(NSError **)error; + +/// The underlying `git_cred` object. +- (git_cred *)git_cred __attribute__((objc_returns_inner_pointer)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTCredential.m b/ObjectiveGit/GTCredential.m similarity index 76% rename from Classes/GTCredential.m rename to ObjectiveGit/GTCredential.m index a82922123..1d3894b44 100644 --- a/Classes/GTCredential.m +++ b/ObjectiveGit/GTCredential.m @@ -6,10 +6,11 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import #import "GTCredential.h" #import "GTCredential+Private.h" -#import +#import "NSError+Git.h" + +#import "git2/errors.h" typedef GTCredential *(^GTCredentialProviderBlock)(GTCredentialType allowedTypes, NSString *URL, NSString *userName); @@ -18,6 +19,7 @@ @interface GTCredentialProvider () @end @implementation GTCredentialProvider + + (instancetype)providerWithBlock:(GTCredentialProviderBlock)credentialBlock { NSParameterAssert(credentialBlock != nil); @@ -69,6 +71,19 @@ + (instancetype)credentialWithUserName:(NSString *)userName publicKeyURL:(NSURL return [[self alloc] initWithGitCred:cred]; } ++ (instancetype)credentialWithUserName:(NSString *)userName publicKeyString:(NSString *)publicKeyString privateKeyString:(NSString *)privateKeyString passphrase:(NSString *)passphrase error:(NSError **)error { + NSParameterAssert(privateKeyString != nil); + + git_cred *cred; + int gitError = git_cred_ssh_key_memory_new(&cred, userName.UTF8String, publicKeyString.UTF8String, privateKeyString.UTF8String, passphrase.UTF8String); + if (gitError != GIT_OK) { + if (error) *error = [NSError git_errorFor:gitError description:@"Failed to create credentials object" failureReason:@"There was an error creating a credential object for username %@ with the provided public/private key pair.\nPublic key: %@", userName, publicKeyString]; + return nil; + } + + return [[self alloc] initWithGitCred:cred]; +} + - (instancetype)initWithGitCred:(git_cred *)cred { NSParameterAssert(cred != nil); self = [self init]; @@ -90,16 +105,16 @@ int GTCredentialAcquireCallback(git_cred **git_cred, const char *url, const char GTCredentialProvider *provider = info->credProvider; if (provider == nil) { - giterr_set_str(GIT_EUSER, "No GTCredentialProvider set, but authentication was requested."); + git_error_set_str(GIT_EUSER, "No GTCredentialProvider set, but authentication was requested."); return GIT_ERROR; } - NSString *URL = (url != NULL ? @(url) : nil); + NSString *URL = (url != NULL ? @(url) : @""); NSString *userName = (username_from_url != NULL ? @(username_from_url) : nil); GTCredential *cred = [provider credentialForType:(GTCredentialType)allowed_types URL:URL userName:userName]; if (cred == nil) { - giterr_set_str(GIT_EUSER, "GTCredentialProvider failed to provide credentials."); + git_error_set_str(GIT_EUSER, "GTCredentialProvider failed to provide credentials."); return GIT_ERROR; } diff --git a/ObjectiveGit/GTDiff+Private.h b/ObjectiveGit/GTDiff+Private.h new file mode 100644 index 000000000..bcf640921 --- /dev/null +++ b/ObjectiveGit/GTDiff+Private.h @@ -0,0 +1,19 @@ +// +// GTDiff+Private.h +// ObjectiveGitFramework +// +// Created by Justin Spahr-Summers on 2014-06-27. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +#import "GTDiff.h" + +@interface GTDiff () + +/// Parses a dictionary of diff options into a `git_diff_options` struct, then +/// provides a pointer to that structure to the given `block`. +/// +/// Returns the result of invoking `block`. ++ (int)handleParsedOptionsDictionary:(NSDictionary * _Nullable)dictionary usingBlock:(int (^ _Nonnull)(git_diff_options * _Null_unspecified optionsStruct))block; + +@end diff --git a/ObjectiveGit/GTDiff.h b/ObjectiveGit/GTDiff.h new file mode 100644 index 000000000..7264b4432 --- /dev/null +++ b/ObjectiveGit/GTDiff.h @@ -0,0 +1,342 @@ +// +// GTDiff.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 29/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/diff.h" +#import "GTDiffDelta.h" + +@class GTDiffDelta; +@class GTRepository; +@class GTTree; +@class GTIndex; + +NS_ASSUME_NONNULL_BEGIN + +/// An `NSNumber` wrapped `GTDiffOptionsFlags` representing any flags you wish to +/// pass into the initialisation. +extern NSString *const GTDiffOptionsFlagsKey; + +/// An `NSNumber` wrapped `NSUInteger` dictating the number of unchanged lines +/// that define the boundary of a hunk (and to display around it). +/// +/// Defaults to 3. +extern NSString *const GTDiffOptionsContextLinesKey; + +/// An `NSNumber` wrapped `NSUInteger` dictating the maximum number of unchanged +/// lines between hunk boundaries before the hunks will be merged. +/// +/// Defaults to 0. +extern NSString *const GTDiffOptionsInterHunkLinesKey; + +/// An `NSString` to prefix old file names with. +/// +/// Defaults to "a". +extern NSString *const GTDiffOptionsOldPrefixKey; + +/// An `NSString` to prefix new file names with. +/// +/// Defaults to "b". +extern NSString *const GTDiffOptionsNewPrefixKey; + +/// An `NSNumber` wrapped `NSUInteger` determining the maximum size (in bytes) +/// of a file to diff. Above this size the file will be treated as binary. +/// +/// Defaults to 512MB. +extern NSString *const GTDiffOptionsMaxSizeKey; + +/// An `NSArray` of `NSStrings`s to limit the diff to specific paths inside the +/// repository. The entries in the array represent either single paths or +/// filename patterns with wildcard matching a la standard shell glob (see +/// http://linux.die.net/man/7/glob for wildcard matching rules). +/// +/// The diff will only contain the files or patterns included in this options +/// array. +/// +/// Defaults to including all files. +extern NSString *const GTDiffOptionsPathSpecArrayKey; + +/// Enum for use as documented in the options dictionary with the +/// `GTDiffOptionsFlagsKey` key. +/// +/// See diff.h for documentation of each individual flag. +typedef NS_OPTIONS(NSInteger, GTDiffOptionsFlags) { + GTDiffOptionsFlagsNormal = GIT_DIFF_NORMAL, + + /* + * Options controlling which files will be in the diff + */ + + GTDiffOptionsFlagsReverse = GIT_DIFF_REVERSE, + GTDiffOptionsFlagsIncludeIgnored = GIT_DIFF_INCLUDE_IGNORED, + GTDiffOptionsFlagsRecurseIgnoredDirs = GIT_DIFF_RECURSE_IGNORED_DIRS, + GTDiffOptionsFlagsIncludeUntracked = GIT_DIFF_INCLUDE_UNTRACKED, + GTDiffOptionsFlagsRecurseUntrackedDirs = GIT_DIFF_RECURSE_UNTRACKED_DIRS, + GTDiffOptionsFlagsIncludeUnmodified = GIT_DIFF_INCLUDE_UNMODIFIED, + GTDiffOptionsFlagsIncludeTypeChange = GIT_DIFF_INCLUDE_TYPECHANGE, + GTDiffOptionsFlagsIncludeTypeChangeTrees = GIT_DIFF_INCLUDE_TYPECHANGE_TREES, + GTDiffOptionsFlagsIgnoreFileMode = GIT_DIFF_IGNORE_FILEMODE, + GTDiffOptionsFlagsIgnoreSubmodules = GIT_DIFF_IGNORE_SUBMODULES, + GTDiffOptionsFlagsIgnoreCase = GIT_DIFF_IGNORE_CASE, + GTDiffOptionsFlagsDisablePathspecMatch = GIT_DIFF_DISABLE_PATHSPEC_MATCH, + GTDiffOptionsFlagsSkipBinaryCheck = GIT_DIFF_SKIP_BINARY_CHECK, + GTDiffOptionsFlagsEnableFastUntrackedDirs = GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS, + + /* + * Options controlling how output will be generated + */ + + GTDiffOptionsFlagsForceText = GIT_DIFF_FORCE_TEXT, + GTDiffOptionsFlagsForceBinary = GIT_DIFF_FORCE_BINARY, + GTDiffOptionsFlagsIgnoreWhitespace = GIT_DIFF_IGNORE_WHITESPACE, + GTDiffOptionsFlagsIgnoreWhitespaceChange = GIT_DIFF_IGNORE_WHITESPACE_CHANGE, + GTDiffOptionsFlagsIgnoreWhitespaceEOL = GIT_DIFF_IGNORE_WHITESPACE_EOL, + GTDiffOptionsFlagsShowUntrackedContent = GIT_DIFF_SHOW_UNTRACKED_CONTENT, + GTDiffOptionsFlagsShowUnmodified = GIT_DIFF_SHOW_UNMODIFIED, + + GTDiffOptionsFlagsPatience = GIT_DIFF_PATIENCE, + GTDiffOptionsFlagsMinimal = GIT_DIFF_MINIMAL, +}; + +/// An `NSNumber` wrapped `GTDiffOptionsFlags` bitmask containing any of the +/// flags documented below. +/// +/// Defualts to `GTDiffFindOptionsFlagsFindRenames`. +extern NSString *const GTDiffFindOptionsFlagsKey; + +/// An `NSNumber` wrapped `NSUInteger` dictating the similarity between files +/// to be considered a rename. +/// +/// This is a value as per the git similarity index and should be between 1 and +/// 100 (0 and above 100 use the default). +/// +/// Defaults to 50. +extern NSString *const GTDiffFindOptionsRenameThresholdKey; + +/// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can +/// be to be eligable as a rename. +/// +/// This is a value as per the git similarity index and should be between 1 and +/// 100 (0 and above 100 use the default). +/// +/// Defaults to 50. +extern NSString *const GTDiffFindOptionsRenameFromRewriteThresholdKey; + +/// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can +/// be to be considered a copy. +/// +/// This is a value as per the git similarity index and should be between 1 and +/// 100 (0 and above 100 use the default). +/// +/// Defaults to 50. +extern NSString *const GTDiffFindOptionsCopyThresholdKey; + +/// An `NSNumber` wrapped `NSUInteger` dictating how similar a modified file can +/// be to be to be broken into a separate deletion and addition pair. +/// +/// This is a value as per the git similarity index and should be between 1 and +/// 100 (0 and above 100 use the default). +/// +/// Defaults to 60. +extern NSString *const GTDiffFindOptionsBreakRewriteThresholdKey; + +/// An `NSNumber` wrapped `NSUInteger` dictating the maximum amount of similarity +/// sources to examine. +/// +/// This is the equivalent of the `diff.renameLimit` config value. +/// +/// Defaults to 200. +extern NSString *const GTDiffFindOptionsRenameLimitKey; + +/// Enum for options passed into `-findSimilarWithOptions:`. +/// +/// For individual case documentation see `diff.h`. +typedef NS_OPTIONS(NSInteger, GTDiffFindOptionsFlags) { + GTDiffFindOptionsFlagsFindRenames = GIT_DIFF_FIND_RENAMES, + GTDiffFindOptionsFlagsFindRenamesFromRewrites = GIT_DIFF_FIND_RENAMES_FROM_REWRITES, + GTDiffFindOptionsFlagsFindCopies = GIT_DIFF_FIND_COPIES, + GTDiffFindOptionsFlagsFindCopiesFromUnmodified = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED, + GTDiffFindOptionsFlagsFindRewrites = GIT_DIFF_FIND_REWRITES, + GTDiffFindOptionsFlagsBreakRewrites = GIT_DIFF_BREAK_REWRITES, + GTDiffFindOptionsFlagsFindAndBreakRewrites = GIT_DIFF_FIND_AND_BREAK_REWRITES, + + GTDiffFindOptionsFlagsFindForUntracked = GIT_DIFF_FIND_FOR_UNTRACKED, + GTDiffFindAll = GIT_DIFF_FIND_ALL, + + GTDiffFindOptionsFlagsIgnoreLeadingWhitespace = GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE, + GTDiffFindOptionsFlagsIgnoreWhitespace = GIT_DIFF_FIND_IGNORE_WHITESPACE, + GTDiffFindOptionsFlagsDontIgnoreWhitespace = GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE, + GTDiffFindOptionsFlagsExactMatchOnly = GIT_DIFF_FIND_EXACT_MATCH_ONLY, + + GTDiffFindOptionsFlagsBreakRewritesForRenamesOnly = GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY, +}; + +/// A class representing a single "diff". +/// +/// Analagous to `git_diff_list` in libgit2, this object represents a list of +/// changes or "deltas", which are represented by `GTDiffDelta` objects. +@interface GTDiff : NSObject + +/// The number of deltas represented by the diff object. +@property (nonatomic, readonly) NSUInteger deltaCount; + +/// Create a diff between 2 `GTTree`s. +/// +/// The 2 trees must be from the same repository, or an exception will be thrown. +/// +/// oldTree - The "left" side of the diff. May be nil to represent an empty +/// tree. +/// newTree - The "right" side of the diff. May be nil to represent an empty +/// tree. +/// repository - The repository to be used for the diff. Cannot be nil. +/// options - A dictionary containing any of the GTDiffOptions key constants, or +/// nil to use the defaults. +/// error - Populated with an `NSError` object on error, if information is +/// available. +/// +/// Returns a newly created `GTDiff` object or nil on error. ++ (instancetype _Nullable)diffOldTree:(GTTree * _Nullable)oldTree withNewTree:(GTTree * _Nullable)newTree inRepository:(GTRepository *)repository options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Create a diff between `GTTree` and `GTIndex`. +/// +/// Both instances must be from the same repository, or an exception will be thrown. +/// +/// oldTree - The "left" side of the diff. May be nil to represent an empty +/// tree. +/// newIndex - The "right" side of the diff. May be nil to represent an empty +/// index. +/// repository - The repository to be used for the diff. Cannot be nil. +/// options - A dictionary containing any of the GTDiffOptions key constants, or +/// nil to use the defaults. +/// error - Populated with an `NSError` object on error, if information is +/// available. +/// +/// Returns a newly created `GTDiff` object or nil on error. ++ (instancetype _Nullable)diffOldTree:(GTTree * _Nullable)oldTree withNewIndex:(GTIndex * _Nullable)newIndex inRepository:(GTRepository *)repository options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Create a diff between two `GTIndex`es. +/// +/// Both instances must be from the same repository, or an exception will be thrown. +/// +/// oldIndex - The "left" side of the diff. May be nil to represent an empty +/// index. +/// newIndex - The "right" side of the diff. May be nil to represent an empty +/// index. +/// repository - The repository to be used for the diff. Cannot be nil. +/// options - A dictionary containing any of the GTDiffOptions key constants, or +/// nil to use the defaults. +/// error - Populated with an `NSError` object on error, if information is +/// available. +/// +/// Returns a newly created `GTDiff` object or nil on error. ++ (instancetype _Nullable)diffOldIndex:(GTIndex * _Nullable)oldIndex withNewIndex:(GTIndex * _Nullable)newIndex inRepository:(GTRepository *)repository options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Create a diff between a repository's current index. +/// +/// This is equivalent to `git diff --cached ` or if you pass the HEAD +/// tree, then `git diff --cached`. +/// +/// The tree you pass will be used for the "left" side of the diff, and the +/// index will be used for the "right" side of the diff. +/// +/// tree - The tree to be diffed. The index will be taken from this tree's +/// repository. The left side of the diff. May be nil to represent an +/// empty tree. +/// repository - The repository to be used for the diff. +/// options - A dictionary containing any of the GTDiffOptions key constants, or +/// nil to use the defaults. +/// error - Populated with an `NSError` object on error, if information is +/// available. +/// +/// Returns a newly created `GTDiff` object or nil on error. ++ (instancetype _Nullable)diffIndexFromTree:(GTTree * _Nullable)tree inRepository:(GTRepository * _Nullable)repository options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Create a diff between the index and working directory in a given repository. +/// +/// This matches the `git diff` command. +/// +/// repository - The repository to be used for the diff. May not be nil. +/// options - A dictionary containing any of the GTDiffOptions key constants, +/// or nil to use the defaults. +/// error - Populated with an `NSError` object on error, if information is +/// available. +/// +/// Returns a newly created `GTDiff` object or nil on error. ++ (instancetype _Nullable)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Create a diff between a repository's working directory and a tree. +/// +/// tree - The tree to be diffed. The tree will be the left side of the diff. +/// May be nil to represent an empty tree. +/// repository - The repository to be used for the diff. May not be nil. +/// options - A dictionary containing any of the GTDiffOptions key constants, or +/// nil to use the defaults. +/// error - Populated with an `NSError` object on error, if information is +/// available. +/// +/// Returns a newly created `GTDiff` object or nil on error. ++ (instancetype _Nullable)diffWorkingDirectoryFromTree:(GTTree * _Nullable)tree inRepository:(GTRepository *)repository options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Create a diff between the working directory and HEAD. +/// +/// If the repository does not have a HEAD commit yet, this will create a diff of +/// the working directory as if everything would be part of the initial commit. +/// +/// repository - The repository to be used for the diff. May not be nil. +/// options - A dictionary containing any of the GTDiffOptions key constants, +/// or nil to use the defaults. +/// error - Populated if an error occurs. +/// +/// Returns a newly created GTDiff, or nil if an error occurred. ++ (instancetype _Nullable)diffWorkingDirectoryToHEADInRepository:(GTRepository *)repository options:(NSDictionary * _Nullable)options error:(NSError **)error; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initialiser. +/// +/// diff - The diff to represent. Cannot be NULL. +/// repository - The repository in which the diff lives. Cannot be nil. +/// +/// Returns the initialized object. +- (instancetype _Nullable)initWithGitDiff:(git_diff *)diff repository:(GTRepository *)repository NS_DESIGNATED_INITIALIZER; + +/// The libgit2 diff object. +- (git_diff *)git_diff __attribute__((objc_returns_inner_pointer)); + +/// The number of deltas of the given type that are contained in the diff. +- (NSUInteger)numberOfDeltasWithType:(GTDeltaType)deltaType; + +/// Enumerate the deltas in a diff. +/// +/// It is worth noting that the `git_diff_patch` objects backing each delta +/// contain the entire contents in memory. It is therefore recommended you +/// do not store the `delta` object given here, but instead perform any work +/// necessary within the provided block. +/// +/// Also note that this method blocks during the enumeration. +/// +/// block - A block to be executed for each delta. Setting `stop` to `YES` +/// immediately stops the enumeration. May not be nil. +- (void)enumerateDeltasUsingBlock:(void (^)(GTDiffDelta *delta, BOOL *stop))block; + +/// Modify the diff list to combine similar changes using the given options. +/// +/// options - A dictionary containing any of the GTDiffFindOptions key constants +/// or nil to use the defaults. +- (void)findSimilarWithOptions:(NSDictionary * _Nullable)options; + +/// Merge a diff with another diff. +/// +/// diff - the diff to merge in. +/// error - Populated if an error occurs +/// +/// Returns YES if the merge was successfull, and NO and sets `error` otherwise. +- (BOOL)mergeDiffWithDiff:(GTDiff *)diff error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTDiff.m b/ObjectiveGit/GTDiff.m similarity index 74% rename from Classes/GTDiff.m rename to ObjectiveGit/GTDiff.m index 70fc9bc6a..b3de61898 100644 --- a/Classes/GTDiff.m +++ b/ObjectiveGit/GTDiff.m @@ -8,15 +8,17 @@ #import "GTDiff.h" +#import "GTCommit.h" #import "GTRepository.h" #import "GTTree.h" -#import "GTCommit.h" - +#import "GTIndex.h" #import "NSArray+StringArray.h" #import "NSError+Git.h" #import "EXTScope.h" +#import "git2/errors.h" + NSString *const GTDiffOptionsFlagsKey = @"GTDiffOptionsFlagsKey"; NSString *const GTDiffOptionsContextLinesKey = @"GTDiffOptionsContextLinesKey"; NSString *const GTDiffOptionsInterHunkLinesKey = @"GTDiffOptionsInterHunkLinesKey"; @@ -78,7 +80,7 @@ + (int)handleParsedOptionsDictionary:(NSDictionary *)dictionary usingBlock:(int return block(optionsPtr); } -+ (GTDiff *)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { ++ (instancetype)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { NSParameterAssert(repository != nil); __block git_diff *diff; @@ -90,10 +92,41 @@ + (GTDiff *)diffOldTree:(GTTree *)oldTree withNewTree:(GTTree *)newTree inReposi return nil; } - return [[GTDiff alloc] initWithGitDiff:diff repository:repository]; + return [[self alloc] initWithGitDiff:diff repository:repository]; +} + ++ (instancetype)diffOldTree:(GTTree *)oldTree withNewIndex:(GTIndex *)newIndex inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { + NSParameterAssert(repository != nil); + + __block git_diff *diff; + int status = [self handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_diff_tree_to_index(&diff, repository.git_repository, oldTree.git_tree, newIndex.git_index, optionsStruct); + }]; + if (status != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to create diff between %@ and %@", oldTree.SHA, newIndex]; + return nil; + } + + return [[self alloc] initWithGitDiff:diff repository:repository]; +} + ++ (instancetype)diffOldIndex:(GTIndex *)oldIndex withNewIndex:(GTIndex *)newIndex inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error +{ + NSParameterAssert(repository != nil); + + __block git_diff *diff; + int status = [self handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_diff_index_to_index(&diff, repository.git_repository, oldIndex.git_index, newIndex.git_index, optionsStruct); + }]; + if (status != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to create diff between %@ and %@", oldIndex, newIndex]; + return nil; + } + + return [[self alloc] initWithGitDiff:diff repository:repository]; } -+ (GTDiff *)diffIndexFromTree:(GTTree *)tree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { ++ (instancetype)diffIndexFromTree:(GTTree *)tree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { NSParameterAssert(repository != nil); NSParameterAssert(tree == nil || [tree.repository isEqual:repository]); @@ -106,10 +139,10 @@ + (GTDiff *)diffIndexFromTree:(GTTree *)tree inRepository:(GTRepository *)reposi return nil; } - return [[GTDiff alloc] initWithGitDiff:diff repository:repository]; + return [[self alloc] initWithGitDiff:diff repository:repository]; } -+ (GTDiff *)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { ++ (instancetype)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { NSParameterAssert(repository != nil); __block git_diff *diff; @@ -121,10 +154,10 @@ + (GTDiff *)diffIndexToWorkingDirectoryInRepository:(GTRepository *)repository o return nil; } - return [[GTDiff alloc] initWithGitDiff:diff repository:repository]; + return [[self alloc] initWithGitDiff:diff repository:repository]; } -+ (GTDiff *)diffWorkingDirectoryFromTree:(GTTree *)tree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { ++ (instancetype)diffWorkingDirectoryFromTree:(GTTree *)tree inRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { NSParameterAssert(repository != nil); NSParameterAssert(tree == nil || [tree.repository isEqual:repository]); @@ -137,17 +170,17 @@ + (GTDiff *)diffWorkingDirectoryFromTree:(GTTree *)tree inRepository:(GTReposito return nil; } - return [[GTDiff alloc] initWithGitDiff:diff repository:repository]; + return [[self alloc] initWithGitDiff:diff repository:repository]; } -+ (GTDiff *)diffWorkingDirectoryToHEADInRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { ++ (instancetype)diffWorkingDirectoryToHEADInRepository:(GTRepository *)repository options:(NSDictionary *)options error:(NSError **)error { NSParameterAssert(repository != nil); GTCommit *HEADCommit = [[repository headReferenceWithError:NULL] resolvedTarget]; - GTDiff *HEADIndexDiff = [GTDiff diffIndexFromTree:HEADCommit.tree inRepository:repository options:options error:error]; + GTDiff *HEADIndexDiff = [self diffIndexFromTree:HEADCommit.tree inRepository:repository options:options error:error]; if (HEADIndexDiff == nil) return nil; - GTDiff *WDDiff = [GTDiff diffIndexToWorkingDirectoryInRepository:repository options:options error:error]; + GTDiff *WDDiff = [self diffIndexToWorkingDirectoryInRepository:repository options:options error:error]; if (WDDiff == nil) return nil; git_diff_merge(HEADIndexDiff.git_diff, WDDiff.git_diff); @@ -155,6 +188,11 @@ + (GTDiff *)diffWorkingDirectoryToHEADInRepository:(GTRepository *)repository op return HEADIndexDiff; } +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitDiff:(git_diff *)diff repository:(GTRepository *)repository { NSParameterAssert(diff != NULL); NSParameterAssert(repository != nil); @@ -196,7 +234,7 @@ - (NSUInteger)deltaCount { return git_diff_num_deltas(self.git_diff); } -- (NSUInteger)numberOfDeltasWithType:(GTDiffDeltaType)deltaType { +- (NSUInteger)numberOfDeltasWithType:(GTDeltaType)deltaType { return git_diff_num_deltas_of_type(self.git_diff, (git_delta_t)deltaType); } diff --git a/ObjectiveGit/GTDiffDelta.h b/ObjectiveGit/GTDiffDelta.h new file mode 100644 index 000000000..35b8969b3 --- /dev/null +++ b/ObjectiveGit/GTDiffDelta.h @@ -0,0 +1,144 @@ +// +// GTDiffDelta.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/diff.h" +#import "GTDiffFile.h" + +@class GTBlob; +@class GTDiff; +@class GTDiffHunk; +@class GTDiffPatch; + +/// The type of change that this delta represents. +/// +/// GTDeltaTypeUnmodified - No Change. +/// GTDeltaTypeAdded - The file was added to the index. +/// GTDeltaTypeDeleted - The file was removed from the working directory. +/// GTDeltaTypeModified - The file was modified. +/// GTDeltaTypeRenamed - The file has been renamed. +/// GTDeltaTypeCopied - The file was duplicated. +/// GTDeltaTypeIgnored - The file was ignored by git. +/// GTDeltaTypeUntracked - The file has been added to the working directory +/// and is therefore currently untracked. +/// GTDeltaTypeTypeChange - The file has changed from a blob to either a +/// submodule, symlink or directory. Or vice versa. +/// GTDeltaTypeConflicted - The file is conflicted in the working directory. +typedef NS_ENUM(NSInteger, GTDeltaType) { + GTDeltaTypeUnmodified = GIT_DELTA_UNMODIFIED, + GTDeltaTypeAdded = GIT_DELTA_ADDED, + GTDeltaTypeDeleted = GIT_DELTA_DELETED, + GTDeltaTypeModified = GIT_DELTA_MODIFIED, + GTDeltaTypeRenamed = GIT_DELTA_RENAMED, + GTDeltaTypeCopied = GIT_DELTA_COPIED, + GTDeltaTypeIgnored = GIT_DELTA_IGNORED, + GTDeltaTypeUntracked = GIT_DELTA_UNTRACKED, + GTDeltaTypeTypeChange = GIT_DELTA_TYPECHANGE, + GTDeltaTypeUnreadable = GIT_DELTA_UNREADABLE, + GTDeltaTypeConflicted = GIT_DELTA_CONFLICTED, +}; + +NS_ASSUME_NONNULL_BEGIN + +/// A class representing a single change within a diff. +/// +/// The change may not be simply a change of text within a given file, it could +/// be that the file was renamed, or added to the index. See `GTDeltaType` +/// for the types of change represented. +@interface GTDiffDelta : NSObject + +/// The `git_diff_delta` represented by the receiver. +@property (nonatomic, assign, readonly) git_diff_delta git_diff_delta; + +/// Any flags set on the delta. See `GTDiffFileFlag` for more info. +/// +/// Note that this may not include `GTDiffFileFlagBinary` _or_ +/// `GTDiffFileFlagNotBinary` until the content is loaded for this delta (e.g., +/// through a call to -generatePatch:). +@property (nonatomic, assign, readonly) GTDiffFileFlag flags; + +/// The file to the "left" of the diff. +@property (nonatomic, readonly, copy) GTDiffFile * _Nullable oldFile; + +/// The file to the "right" of the diff. +@property (nonatomic, readonly, copy) GTDiffFile * _Nullable newFile __attribute__((ns_returns_not_retained)); + +/// The type of change that this delta represents. +/// +/// Think "status" as in `git status`. +@property (nonatomic, readonly) GTDeltaType type; + +@property (nonatomic, readonly, assign) double similarity; + +/// Diffs the given blob and data buffer. +/// +/// oldBlob - The blob which should comprise the left side of the diff. May be +/// nil to represent an empty blob. +/// oldBlobPath - The path to which `oldBlob` corresponds. May be nil. +/// newBlob - The blob which should comprise the right side of the diff. May be +/// nil to represent an empty blob. +/// newBlobPath - The path to which `newBlob` corresponds. May be nil. +/// options - A dictionary containing any of the above options key constants, +// or nil to use the defaults. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns a diff delta, or nil if an error occurs. ++ (instancetype _Nullable)diffDeltaFromBlob:(GTBlob * _Nullable)oldBlob forPath:(NSString * _Nullable)oldBlobPath toBlob:(GTBlob * _Nullable)newBlob forPath:(NSString * _Nullable)newBlobPath options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Diffs the given blob and data buffer. +/// +/// blob - The blob which should comprise the left side of the diff. May be +/// nil to represent an empty blob. +/// blobPath - The path to which `blob` corresponds. May be nil. +/// data - The data which should comprise the right side of the diff. May be +/// nil to represent an empty blob. +/// dataPath - The path to which `data` corresponds. May be nil. +/// options - A dictionary containing any of the above options key constants, +// or nil to use the defaults. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns a diff delta, or nil if an error occurs. ++ (instancetype _Nullable)diffDeltaFromBlob:(GTBlob * _Nullable)blob forPath:(NSString * _Nullable)blobPath toData:(NSData * _Nullable)data forPath:(NSString * _Nullable)dataPath options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Diffs the given data buffers. +/// +/// oldData - The data which should comprise the left side of the diff. May be +/// nil to represent an empty blob. +/// oldDataPath - The path to which `oldData` corresponds. May be nil. +/// newData - The data which should comprise the right side of the diff. May +/// be nil to represent an empty blob. +/// newDataPath - The path to which `newData` corresponds. May be nil. +/// options - A dictionary containing any of the above options key constants, +// or nil to use the defaults. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns a diff delta, or nil if an error occurs. ++ (instancetype _Nullable)diffDeltaFromData:(NSData * _Nullable)oldData forPath:(NSString * _Nullable)oldDataPath toData:(NSData * _Nullable)newData forPath:(NSString * _Nullable)newDataPath options:(NSDictionary * _Nullable)options error:(NSError **)error; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver to wrap the delta at the given index. +/// +/// diff - The diff which contains the delta to wrap. Must not be nil. +/// deltaIndex - The index of the delta within the diff. +/// +/// Returns a diff delta, or nil if an error occurs. +- (instancetype _Nullable)initWithDiff:(GTDiff *)diff deltaIndex:(NSUInteger)deltaIndex; + +/// Creates a patch from a text delta. +/// +/// If the receiver represents a binary delta, this method will return an error. +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns a new patch, or nil if an error occurs. +- (GTDiffPatch * _Nullable)generatePatch:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTDiffDelta.m b/ObjectiveGit/GTDiffDelta.m new file mode 100644 index 000000000..28358f14f --- /dev/null +++ b/ObjectiveGit/GTDiffDelta.m @@ -0,0 +1,186 @@ +// +// GTDiffDelta.m +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "GTDiffDelta.h" + +#import "GTBlob.h" +#import "GTDiff+Private.h" +#import "GTDiffFile.h" +#import "GTDiffPatch.h" +#import "NSError+Git.h" + +#import "git2/errors.h" + +@interface GTDiffDelta () + +/// Used to dynamically access the underlying `git_diff_delta`. +@property (nonatomic, copy, readonly) git_diff_delta (^deltaAccessor)(void); + +/// Used to generate a patch from this delta. +@property (nonatomic, copy, readonly) int (^patchGenerator)(git_patch **patch); + +/// Initializes the diff delta with blocks that will fulfill its contract. +/// +/// deltaAccessor - A block that will return the `git_diff_delta` underlying +/// this object. Must not be nil. +/// patchGenerator - A block that will be used to lazily generate a patch for +/// the given diff delta. Must not be nil. +/// +/// This is the designated initializer for this class. +- (instancetype)initWithGitDiffDeltaBlock:(git_diff_delta (^)(void))deltaAccessor patchGeneratorBlock:(int (^)(git_patch **patch))patchGenerator NS_DESIGNATED_INITIALIZER; + +@end + +@implementation GTDiffDelta + +#pragma mark Properties + +- (git_diff_delta)git_diff_delta { + return self.deltaAccessor(); +} + +- (GTDiffFileFlag)flags { + return (GTDiffFileFlag)self.git_diff_delta.flags; +} + +- (GTDiffFile *)oldFile { + return [[GTDiffFile alloc] initWithGitDiffFile:self.git_diff_delta.old_file]; +} + +- (GTDiffFile *)newFile { + return [[GTDiffFile alloc] initWithGitDiffFile:self.git_diff_delta.new_file]; +} + +- (GTDeltaType)type { + return (GTDeltaType)self.git_diff_delta.status; +} + +- (double)similarity { + return (double)(self.git_diff_delta.similarity / 100.0); +} + +#pragma mark Lifecycle + +static int GTDiffDeltaCallback(const git_diff_delta *delta, float progress, void *payload) { + git_diff_delta *storage = payload; + *storage = *delta; + + return GIT_OK; +} + ++ (instancetype)diffDeltaFromBlob:(GTBlob *)oldBlob forPath:(NSString *)oldBlobPath toBlob:(GTBlob *)newBlob forPath:(NSString *)newBlobPath options:(NSDictionary *)options error:(NSError **)error { + __block git_diff_delta diffDelta; + + int returnValue = [GTDiff handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_diff_blobs(oldBlob.git_blob, oldBlobPath.UTF8String, newBlob.git_blob, newBlobPath.UTF8String, optionsStruct, >DiffDeltaCallback, NULL, NULL, NULL, &diffDelta); + }]; + + if (returnValue != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:returnValue description:@"Failed to create diff delta between blob %@ at path %@ and blob %@ at path %@", oldBlob.SHA, oldBlobPath, newBlob.SHA, newBlobPath]; + return nil; + } + + return [[self alloc] initWithGitDiffDeltaBlock:^{ + return diffDelta; + } patchGeneratorBlock:^(git_patch **patch) { + return [GTDiff handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_patch_from_blobs(patch, oldBlob.git_blob, oldBlobPath.UTF8String, newBlob.git_blob, newBlobPath.UTF8String, optionsStruct); + }]; + }]; +} + ++ (instancetype)diffDeltaFromBlob:(GTBlob *)blob forPath:(NSString *)blobPath toData:(NSData *)data forPath:(NSString *)dataPath options:(NSDictionary *)options error:(NSError **)error { + __block git_diff_delta diffDelta; + + int returnValue = [GTDiff handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_diff_blob_to_buffer(blob.git_blob, blobPath.UTF8String, data.bytes, data.length, dataPath.UTF8String, optionsStruct, >DiffDeltaCallback, NULL, NULL, NULL, &diffDelta); + }]; + + if (returnValue != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:returnValue description:@"Failed to create diff delta between blob %@ at path %@ and data at path %@", blob.SHA, blobPath, dataPath]; + return nil; + } + + return [[self alloc] initWithGitDiffDeltaBlock:^{ + return diffDelta; + } patchGeneratorBlock:^(git_patch **patch) { + return [GTDiff handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_patch_from_blob_and_buffer(patch, blob.git_blob, blobPath.UTF8String, data.bytes, data.length, dataPath.UTF8String, optionsStruct); + }]; + }]; +} + ++ (instancetype)diffDeltaFromData:(NSData *)oldData forPath:(NSString *)oldDataPath toData:(NSData *)newData forPath:(NSString *)newDataPath options:(NSDictionary *)options error:(NSError **)error { + __block git_diff_delta diffDelta; + + int returnValue = [GTDiff handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_diff_buffers(oldData.bytes, oldData.length, oldDataPath.UTF8String, newData.bytes, newData.length, newDataPath.UTF8String, optionsStruct, >DiffDeltaCallback, NULL, NULL, NULL, &diffDelta); + }]; + + if (returnValue != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:returnValue description:@"Failed to create diff delta between data at path %@ and data at path %@", oldDataPath, newDataPath]; + return nil; + } + + return [[self alloc] initWithGitDiffDeltaBlock:^{ + return diffDelta; + } patchGeneratorBlock:^(git_patch **patch) { + return [GTDiff handleParsedOptionsDictionary:options usingBlock:^(git_diff_options *optionsStruct) { + return git_patch_from_buffers(patch, oldData.bytes, oldData.length, oldDataPath.UTF8String, newData.bytes, newData.length, newDataPath.UTF8String, optionsStruct); + }]; + }]; +} + +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithDiff:(GTDiff *)diff deltaIndex:(NSUInteger)deltaIndex { + NSCParameterAssert(diff != nil); + + return [self initWithGitDiffDeltaBlock:^{ + return *(git_diff_get_delta(diff.git_diff, deltaIndex)); + } patchGeneratorBlock:^(git_patch **patch) { + return git_patch_from_diff(patch, diff.git_diff, deltaIndex); + }]; +} + +- (instancetype)initWithGitDiffDeltaBlock:(git_diff_delta (^)(void))deltaAccessor patchGeneratorBlock:(int (^)(git_patch **patch))patchGenerator { + NSCParameterAssert(deltaAccessor != nil); + NSCParameterAssert(patchGenerator != nil); + + self = [super init]; + if (self == nil) return nil; + + _deltaAccessor = [deltaAccessor copy]; + _patchGenerator = [patchGenerator copy]; + + return self; +} + +#pragma mark Patch Generation + +- (GTDiffPatch *)generatePatch:(NSError **)error { + git_patch *patch = NULL; + int gitError = self.patchGenerator(&patch); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Patch generation failed for delta %@", self]; + return nil; + } + + return [[GTDiffPatch alloc] initWithGitPatch:patch delta:self]; +} + +#pragma mark NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p>{ flags: %u, oldFile: %@, newFile: %@ }", self.class, self, (unsigned)self.git_diff_delta.flags, self.oldFile, self.newFile]; +} + +@end diff --git a/ObjectiveGit/GTDiffFile.h b/ObjectiveGit/GTDiffFile.h new file mode 100644 index 000000000..e88935ffb --- /dev/null +++ b/ObjectiveGit/GTDiffFile.h @@ -0,0 +1,62 @@ +// +// GTDiffFile.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/diff.h" + +/// Flags which may be set on the file. +/// +/// GTDiffFileFlagBinaryMask - A mask to just retrieve the binary/not binary +/// information from a set of flags. +/// +/// See diff.h for further documentation. +typedef NS_OPTIONS(NSInteger, GTDiffFileFlag) { + GTDiffFileFlagValidID = GIT_DIFF_FLAG_VALID_ID, + GTDiffFileFlagBinary = GIT_DIFF_FLAG_BINARY, + GTDiffFileFlagNotBinary = GIT_DIFF_FLAG_NOT_BINARY, + + GTDiffFileFlagBinaryMask = GTDiffFileFlagBinary | GTDiffFileFlagNotBinary, +}; + +@class GTOID; + +NS_ASSUME_NONNULL_BEGIN + +/// A class representing a file on one side of a diff. +@interface GTDiffFile : NSObject + +/// The location within the working directory of the file. +@property (nonatomic, readonly, copy) NSString *path; + +/// The size (in bytes) of the file. +@property (nonatomic, readonly) NSUInteger size; + +/// Any flags set on the file (see `GTDiffFileFlag` for more info). +@property (nonatomic, readonly) GTDiffFileFlag flags; + +/// The mode of the file. +@property (nonatomic, readonly) mode_t mode; + +/// The OID for the file. +@property (nonatomic, readonly, copy) GTOID * _Nullable OID; + +/// The git_diff_file represented by the receiver. +@property (nonatomic, readonly) git_diff_file git_diff_file; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver with the provided libgit2 object. Designated initializer. +/// +/// file - The git_diff_file wrapped by the receiver. +/// +/// Returns an initialized GTDiffFile. +- (instancetype _Nullable)initWithGitDiffFile:(git_diff_file)file NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTDiffFile.m b/ObjectiveGit/GTDiffFile.m similarity index 71% rename from Classes/GTDiffFile.m rename to ObjectiveGit/GTDiffFile.m index 1b890b226..24b3c7b11 100644 --- a/Classes/GTDiffFile.m +++ b/ObjectiveGit/GTDiffFile.m @@ -11,14 +11,20 @@ @implementation GTDiffFile +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitDiffFile:(git_diff_file)file { NSParameterAssert(file.path != NULL); self = [super init]; if (self == nil) return nil; - _path = @(file.path); - if (_path == nil) return nil; + NSString *path = @(file.path); + if (path == nil) return nil; + _path = path; _git_diff_file = file; _size = (NSUInteger)file.size; @@ -29,7 +35,7 @@ - (instancetype)initWithGitDiffFile:(git_diff_file)file { } - (NSString *)debugDescription { - return [NSString stringWithFormat:@"%@ path: %@, size: %ld, mode: %u, flags: %u", super.debugDescription, self.path, (unsigned long)self.size, self.mode, self.flags]; + return [NSString stringWithFormat:@"%@ path: %@, size: %ld, mode: %u, flags: %@", super.debugDescription, self.path, (unsigned long)self.size, self.mode, @(self.flags)]; } - (GTOID *)OID { diff --git a/ObjectiveGit/GTDiffHunk.h b/ObjectiveGit/GTDiffHunk.h new file mode 100644 index 000000000..164185e58 --- /dev/null +++ b/ObjectiveGit/GTDiffHunk.h @@ -0,0 +1,64 @@ +// +// GTDiffHunk.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 30/11/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import + +@class GTDiffLine; +@class GTDiffPatch; + +NS_ASSUME_NONNULL_BEGIN + +/// A class representing a hunk within a diff patch. +@interface GTDiffHunk : NSObject + +/// The header of the hunk. +@property (nonatomic, readonly, copy) NSString *header; + +/// The number of lines represented in the hunk. +@property (nonatomic, readonly) NSUInteger lineCount; + +/// The starting line number in the old file +@property (nonatomic, readonly) NSUInteger oldStart; + +/// The number of lines in the old file +@property (nonatomic, readonly) NSUInteger oldLines; + +/// The starting line number in the new file +@property (nonatomic, readonly) NSUInteger newStart; + +/// The number of lines in the new file +@property (nonatomic, readonly) NSUInteger newLines; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initialiser. +/// +/// The contents of a hunk are lazily loaded, therefore we initialise the object +/// simply with the patch it originates from and which hunk index it represents. +/// +/// patch - The patch the hunk originates from. Must not be nil. +/// hunkIndex - The hunk's index within the patch. +/// +/// Returns the initialized instance. +- (instancetype _Nullable)initWithPatch:(GTDiffPatch *)patch hunkIndex:(NSUInteger)hunkIndex NS_DESIGNATED_INITIALIZER; + +/// Perfoms the given block on each line in the hunk. +/// +/// Note that this method blocks during the enumeration. +/// +/// error - A pointer to an NSError that will be set if one occurs. +/// block - A block to execute on each line. Setting `stop` to `NO` will +/// immediately stop the enumeration and return from the method. Must not +/// be nil. +/// Return YES if the enumeration was successful, NO otherwise (and an error will +/// be set in `error`). +- (BOOL)enumerateLinesInHunk:(NSError **)error usingBlock:(void (^)(GTDiffLine *line, BOOL *stop))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTDiffHunk.m b/ObjectiveGit/GTDiffHunk.m similarity index 74% rename from Classes/GTDiffHunk.m rename to ObjectiveGit/GTDiffHunk.m index 69decc8a3..0b822903f 100644 --- a/Classes/GTDiffHunk.m +++ b/ObjectiveGit/GTDiffHunk.m @@ -12,6 +12,9 @@ #import "GTDiffPatch.h" #import "NSError+Git.h" +#import "git2/errors.h" +#import "git2/patch.h" + @interface GTDiffHunk () @property (nonatomic, assign, readonly) const git_diff_hunk *git_hunk; @@ -22,6 +25,11 @@ @interface GTDiffHunk () @implementation GTDiffHunk +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithPatch:(GTDiffPatch *)patch hunkIndex:(NSUInteger)hunkIndex { NSParameterAssert(patch != nil); @@ -32,10 +40,16 @@ - (instancetype)initWithPatch:(GTDiffPatch *)patch hunkIndex:(NSUInteger)hunkInd int result = git_patch_get_hunk(&_git_hunk, &gitLineCount, patch.git_patch, hunkIndex); if (result != GIT_OK) return nil; _lineCount = gitLineCount; + _oldStart = self.git_hunk->old_start; + _oldLines = self.git_hunk->old_lines; + _newStart = self.git_hunk->new_start; + _newLines = self.git_hunk->new_lines; _patch = patch; _hunkIndex = hunkIndex; - _header = [[[NSString alloc] initWithBytes:self.git_hunk->header length:self.git_hunk->header_len encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.newlineCharacterSet]; + NSString *hunkHeader = [[[NSString alloc] initWithBytes:self.git_hunk->header length:self.git_hunk->header_len encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.newlineCharacterSet]; + NSAssert(hunkHeader != nil, @"Failed to build hunk header"); + _header = hunkHeader; return self; } diff --git a/ObjectiveGit/GTDiffLine.h b/ObjectiveGit/GTDiffLine.h new file mode 100644 index 000000000..26dc1949f --- /dev/null +++ b/ObjectiveGit/GTDiffLine.h @@ -0,0 +1,59 @@ +// +// GTDiffLine.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 20/12/2012. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/diff.h" + +/// A character representing the origin of a given line. +/// +/// See diff.h for individual documentation. +typedef NS_ENUM(char, GTDiffLineOrigin) { + GTDiffLineOriginContext = GIT_DIFF_LINE_CONTEXT, + GTDiffLineOriginAddition = GIT_DIFF_LINE_ADDITION, + GTDiffLineOriginDeletion = GIT_DIFF_LINE_DELETION, + GTDiffLineOriginNoEOFNewlineContext = GIT_DIFF_LINE_CONTEXT_EOFNL, + GTDiffLineOriginAddEOFNewLine = GIT_DIFF_LINE_ADD_EOFNL, + GTDiffLineOriginDeleteEOFNewLine = GIT_DIFF_LINE_DEL_EOFNL, +}; + +NS_ASSUME_NONNULL_BEGIN + +/// Represents an individual line in a diff hunk. +@interface GTDiffLine : NSObject + +/// The content string of the line. +@property (nonatomic, readonly, copy) NSString *content; + +/// The line number of this line in the left side of the diff. +/// +/// -1 if the line is an addition. +@property (nonatomic, readonly) NSInteger oldLineNumber; + +/// The line number of this line in the right side of the diff. +/// +/// -1 if the line is a deletion. +@property (nonatomic, readonly) NSInteger newLineNumber; + +/// The origin of the line, see the enum above for possible values. +@property (nonatomic, readonly) GTDiffLineOrigin origin; + +/// The number of newlines appearing in `-content`. +@property (nonatomic, readonly) NSInteger lineCount; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initialiser. +/// +/// line - The diff line to wrap. May not be NULL. +/// +/// Returns a diff line, or nil if an error occurs. +- (instancetype _Nullable)initWithGitLine:(const git_diff_line *)line NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTDiffLine.m b/ObjectiveGit/GTDiffLine.m similarity index 93% rename from Classes/GTDiffLine.m rename to ObjectiveGit/GTDiffLine.m index 36df18c6e..1d20211cd 100644 --- a/Classes/GTDiffLine.m +++ b/ObjectiveGit/GTDiffLine.m @@ -10,6 +10,11 @@ @implementation GTDiffLine +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitLine:(const git_diff_line *)line { self = [super init]; if (self == nil) return nil; diff --git a/ObjectiveGit/GTDiffPatch.h b/ObjectiveGit/GTDiffPatch.h new file mode 100644 index 000000000..a76b6de9e --- /dev/null +++ b/ObjectiveGit/GTDiffPatch.h @@ -0,0 +1,74 @@ +// +// GTDiffPatch.h +// ObjectiveGitFramework +// +// Created by Justin Spahr-Summers on 2014-02-27. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/patch.h" + +@class GTDiffHunk; +@class GTDiffDelta; + +NS_ASSUME_NONNULL_BEGIN + +/// Represents one or more text changes to a single file within a diff. +@interface GTDiffPatch : NSObject + +/// The delta corresponding to this patch. +@property (nonatomic, strong, readonly) GTDiffDelta *delta; + +/// The number of added lines in this patch. +@property (nonatomic, assign, readonly) NSUInteger addedLinesCount; + +/// The number of deleted lines in this patch. +@property (nonatomic, assign, readonly) NSUInteger deletedLinesCount; + +/// The number of context lines in this patch. +@property (nonatomic, assign, readonly) NSUInteger contextLinesCount; + +/// The number of hunks in this patch. +@property (nonatomic, readonly) NSUInteger hunkCount; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver to wrap the given patch. Designated initializer. +/// +/// patch - The patch object to wrap and take ownership of. This will +/// automatically be freed when the receiver is deallocated. Must not be +/// NULL. +/// delta - The diff delta corresponding to this patch. Must not be nil. +- (instancetype _Nullable)initWithGitPatch:(git_patch *)patch delta:(GTDiffDelta *)delta NS_DESIGNATED_INITIALIZER; + +/// Returns the underlying patch object. +- (git_patch *)git_patch __attribute__((objc_returns_inner_pointer)); + +/// Get the size of this patch. +/// +/// includeContext - Whether to include the context lines in the size. +/// includeHunkHeaders - Whether to include the hunk header lines in the size. +/// includeFileHeaders - Whether to include the file header lines in the size. +/// +/// Returns the raw size of the delta, in bytes. +- (NSUInteger)sizeWithContext:(BOOL)includeContext hunkHeaders:(BOOL)includeHunkHeaders fileHeaders:(BOOL)includeFileHeaders; + +/// Returns the raw patch data. +- (NSData *)patchData; + +/// Enumerate the hunks contained in the patch. +/// +/// This enumeration is synchronous, and will block the calling thread while +/// generating hunk content. +/// +/// block - A block to be executed for each hunk. Setting `stop` to `YES` +/// will stop the enumeration after the block returns. May not be nil. +/// +/// Returns whether enumeration was successful, or terminated early. If `NO`, an +/// error occurred during enumeration. +- (BOOL)enumerateHunksUsingBlock:(void (^)(GTDiffHunk *hunk, BOOL *stop))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTDiffPatch.m b/ObjectiveGit/GTDiffPatch.m similarity index 83% rename from Classes/GTDiffPatch.m rename to ObjectiveGit/GTDiffPatch.m index 2136e3add..715db5a13 100644 --- a/Classes/GTDiffPatch.m +++ b/ObjectiveGit/GTDiffPatch.m @@ -20,6 +20,11 @@ @implementation GTDiffPatch #pragma mark Lifecycle +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitPatch:(git_patch *)patch delta:(GTDiffDelta *)delta { NSParameterAssert(patch != NULL); NSParameterAssert(delta != nil); @@ -59,6 +64,16 @@ - (NSUInteger)sizeWithContext:(BOOL)includeContext hunkHeaders:(BOOL)includeHunk return git_patch_size(self.git_patch, includeContext, includeHunkHeaders, includeFileHeaders); } +- (NSData *)patchData { + git_buf buf = GIT_BUF_INIT_CONST(0, NULL); + git_patch_to_buf(&buf, self.git_patch); + + NSData *buffer = [[NSData alloc] initWithBytes:buf.ptr length:buf.size]; + git_buf_dispose(&buf); + + return buffer; +} + #pragma mark Hunks - (BOOL)enumerateHunksUsingBlock:(void (^)(GTDiffHunk *hunk, BOOL *stop))block { diff --git a/ObjectiveGit/GTEnumerator.h b/ObjectiveGit/GTEnumerator.h new file mode 100644 index 000000000..4d8b27201 --- /dev/null +++ b/ObjectiveGit/GTEnumerator.h @@ -0,0 +1,184 @@ +// +// GTEnumerator.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 2/21/11. +// +// The MIT License +// +// Copyright (c) 2011 Tim Clem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "GTObject.h" +#import "git2/revwalk.h" + +/// Options to specify enumeration order when enumerating through a repository. +/// With the exception of GTEnumeratorOptionsNone, the values here can be ORed +/// together to combine their behaviors. +/// +/// GTEnumeratorOptionsNone - Implementation-defined sorting. +/// GTEnumeratorOptionsTopologicalSort - Sort parents before children. +/// GTEnumeratorOptionsTimeSort - Sort by commit time. +/// GTEnumeratorOptionsReverse - Iterate in reverse order. +typedef NS_OPTIONS(unsigned int, GTEnumeratorOptions) { + GTEnumeratorOptionsNone = GIT_SORT_NONE, + GTEnumeratorOptionsTopologicalSort = GIT_SORT_TOPOLOGICAL, + GTEnumeratorOptionsTimeSort = GIT_SORT_TIME, + GTEnumeratorOptionsReverse = GIT_SORT_REVERSE, +}; + +@class GTRepository; +@class GTCommit; + +NS_ASSUME_NONNULL_BEGIN + +/// Enumerates the commits in a repository. +@interface GTEnumerator : NSEnumerator + +/// The repository being enumerated. +@property (nonatomic, strong, readonly) GTRepository *repository; + +/// The options currently being used for enumeration. +/// +/// To set new options, use -resetWithOptions:. +@property (nonatomic, assign, readonly) GTEnumeratorOptions options; + +- (instancetype)init NS_UNAVAILABLE; + +/// The underlying `git_revwalk` from libgit2. +- (git_revwalk *)git_revwalk __attribute__((objc_returns_inner_pointer)); + +/// Initializes the receiver to enumerate the commits in the given repository. Designated initializer. +/// +/// repo - The repository to enumerate the commits of. This must not be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns an initialized enumerator, or nil if an error occurs. +- (id _Nullable)initWithRepository:(GTRepository *)repo error:(NSError **)error NS_DESIGNATED_INITIALIZER; + +/// Marks a commit to start traversal from. +/// +/// sha - The SHA of a commit in the receiver's repository. This must not be +/// nil. +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether pushing the commit was successful. +- (BOOL)pushSHA:(NSString *)sha error:(NSError **)error; + +/// Pushes all references matching `refGlob`. +/// +/// refGlob - A glob to match references against. This must not be nil. +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether pushing matching references was successful. +- (BOOL)pushGlob:(NSString *)refGlob error:(NSError **)error; + +/// Push HEAD reference. +/// +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether pushing the HEAD reference was successful. +- (BOOL)pushHEAD:(NSError **)error; + +/// Push a reference by name. +/// +/// refName - The reference name to push. Must not be nil. +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether pushing the reference name was successful. +- (BOOL)pushReferenceName:(NSString *)refName error:(NSError **)error; + +/// Hides the specified commit and all of its ancestors when enumerating. +/// +/// sha - The SHA of a commit in the receiver's repository. This must not be +/// nil. +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether marking the SHA for hiding was successful. +- (BOOL)hideSHA:(NSString *)sha error:(NSError **)error; + +/// Hides all references matching `refGlob`. +/// +/// refGlob - A glob to match references against. This must not be nil. +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether marking matching references for hiding was successful. +- (BOOL)hideGlob:(NSString *)refGlob error:(NSError **)error; + +/// Hide HEAD reference. +/// +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether marking HEAD for hiding was successful. +- (BOOL)hideHEAD:(NSError **)error; + + +/// Hide a reference by name. +/// +/// refName - The reference name to hide. Must not be nil. +/// error - If not NULL, this will be set to any error that occurs. +/// +/// Returns whether hiding the reference name was successful. +- (BOOL)hideReferenceName:(NSString *)refName error:(NSError **)error; + +/// Resets the receiver, putting it back into a clean state for reuse, and +/// replacing the receiver's `options`. +- (void)resetWithOptions:(GTEnumeratorOptions)options; + +/// Enumerates all marked commits, completely exhausting the receiver. +/// +/// error - If not NULL, set to any error that occurs during traversal. +/// +/// Returns a (possibly empty) array of GTCommits, or nil if an error occurs. +- (NSArray * _Nullable)allObjectsWithError:(NSError **)error; + +/// Get the next OID. +/// +/// success - If not NULL, this will be set to whether getting the next object +/// was successful. This will be YES if the receiver is exhausted, so +/// it can be used to interpret the meaning of a nil return value. +/// error - If not NULL, set to any error that occurs during traversal. +/// +/// Returns nil if an error occurs or the enumeration is done. +- (GTOID * _Nullable)nextOIDWithSuccess:(BOOL * _Nullable)success error:(NSError **)error; + +/// Gets the next commit. +/// +/// success - If not NULL, this will be set to whether getting the next object +/// was successful. This will be YES if the receiver is exhausted, so +/// it can be used to interpret the meaning of a nil return value. +/// error - If not NULL, set to any error that occurs during traversal. +/// +/// Returns nil if an error occurs or the receiver is exhausted. +- (GTCommit * _Nullable)nextObjectWithSuccess:(BOOL * _Nullable)success error:(NSError **)error; + +/// Counts the number of commits that were not enumerated, completely exhausting +/// the receiver. +/// +/// error - If not NULL, set to any error that occurs during traversal. +/// +/// Returns the number of commits remaining, or `NSNotFound` if an error occurs. +- (NSUInteger)countRemainingObjects:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTEnumerator.m b/ObjectiveGit/GTEnumerator.m similarity index 70% rename from Classes/GTEnumerator.m rename to ObjectiveGit/GTEnumerator.m index 62a61a8f7..b684d4e3e 100644 --- a/Classes/GTEnumerator.m +++ b/ObjectiveGit/GTEnumerator.m @@ -35,6 +35,8 @@ #import "GTRepository+Private.h" #import "GTOID.h" +#import "git2/errors.h" + @interface GTEnumerator () @property (nonatomic, assign, readonly) git_revwalk *walk; @@ -46,7 +48,16 @@ @implementation GTEnumerator #pragma mark Lifecycle -- (id)initWithRepository:(GTRepository *)repo error:(NSError **)error { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (git_revwalk *)git_revwalk { + return self.walk; +} + +- (instancetype)initWithRepository:(GTRepository *)repo error:(NSError **)error { NSParameterAssert(repo != nil); self = [super init]; @@ -100,6 +111,26 @@ - (BOOL)pushGlob:(NSString *)refGlob error:(NSError **)error { return YES; } +- (BOOL)pushHEAD:(NSError **)error { + int gitError = git_revwalk_push_head(self.walk); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to push HEAD onto rev walker."]; + return NO; + } + return YES; +} + +- (BOOL)pushReferenceName:(NSString *)refName error:(NSError **)error { + NSParameterAssert(refName != nil); + + int gitError = git_revwalk_push_ref(self.walk, refName.UTF8String); + if (gitError != 0) { + if (error) *error = [NSError git_errorFor:gitError description:@"Failed to push reference %@", refName]; + return NO; + } + return YES; +} + - (BOOL)hideSHA:(NSString *)sha error:(NSError **)error { NSParameterAssert(sha != nil); @@ -120,13 +151,33 @@ - (BOOL)hideGlob:(NSString *)refGlob error:(NSError **)error { int gitError = git_revwalk_hide_glob(self.walk, refGlob.UTF8String); if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to push glob %@ onto rev walker.", refGlob]; + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to hide glob %@ in rev walker.", refGlob]; return NO; } return YES; } +- (BOOL)hideHEAD:(NSError **)error { + int gitError = git_revwalk_hide_head(self.walk); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to hide HEAD onto rev walker."]; + return NO; + } + return YES; +} + +- (BOOL)hideReferenceName:(NSString *)refName error:(NSError **)error { + NSParameterAssert(refName != nil); + + int gitError = git_revwalk_hide_ref(self.walk, refName.UTF8String); + if (gitError != 0) { + if (error) *error = [NSError git_errorFor:gitError description:@"Failed to hide reference %@", refName]; + return NO; + } + return YES; +} + #pragma mark Resetting - (void)resetWithOptions:(GTEnumeratorOptions)options { @@ -138,16 +189,34 @@ - (void)resetWithOptions:(GTEnumeratorOptions)options { #pragma mark Enumerating -- (GTCommit *)nextObjectWithSuccess:(BOOL *)success error:(NSError **)error { +- (GTOID *)nextOIDWithSuccess:(BOOL *)success error:(NSError **)error { git_oid oid; + int gitError = git_revwalk_next(&oid, self.walk); if (gitError == GIT_ITEROVER) { if (success != NULL) *success = YES; return nil; } + if (gitError != GIT_OK) { + if (success != NULL) *success = NO; + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Enumeration failed"]; + return nil; + } + + if (success != NULL) *success = YES; + return [GTOID oidWithGitOid:&oid]; +} + +- (GTCommit *)nextObjectWithSuccess:(BOOL *)success error:(NSError **)error { + GTOID *oid = [self nextOIDWithSuccess:success error:error]; + if (oid == nil) { + // We don't care whether the iteration completed, or an error occurred, + // there's nothing to lookup. + return nil; + } // Ignore error if we can't lookup object and just return nil. - GTCommit *commit = [self.repository lookUpObjectByGitOid:&oid objectType:GTObjectTypeCommit error:error]; + GTCommit *commit = [self.repository lookUpObjectByOID:oid objectType:GTObjectTypeCommit error:error]; if (success != NULL) *success = (commit != nil); return commit; } @@ -190,7 +259,8 @@ - (NSUInteger)countRemainingObjects:(NSError **)error { #pragma mark NSEnumerator - (NSArray *)allObjects { - return [self allObjectsWithError:NULL]; + NSArray *objects = [self allObjectsWithError:NULL]; + return objects ? objects : [NSArray array]; } - (id)nextObject { diff --git a/ObjectiveGit/GTFetchHeadEntry.h b/ObjectiveGit/GTFetchHeadEntry.h new file mode 100644 index 000000000..fd6fa466f --- /dev/null +++ b/ObjectiveGit/GTFetchHeadEntry.h @@ -0,0 +1,46 @@ +// +// GTFetchHeadEntry.h +// ObjectiveGitFramework +// +// Created by Pablo Bendersky on 8/14/14. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +#import + +@class GTRepository; +@class GTOID; +@class GTReference; + +NS_ASSUME_NONNULL_BEGIN + +/// A class representing an entry on the FETCH_HEAD file, as returned by the callback of git_repository_fetchhead_foreach. +@interface GTFetchHeadEntry : NSObject + +/// The reference of this fetch entry. +@property (nonatomic, readonly, strong) GTReference *reference; + +/// The remote URL where this entry was originally fetched from. +@property (nonatomic, readonly, copy) NSString *remoteURLString; + +/// The target OID of this fetch entry (what we need to merge with) +@property (nonatomic, readonly, copy) GTOID *targetOID; + +/// Flag indicating if we need to merge this entry or not. +@property (nonatomic, getter = isMerge, readonly) BOOL merge; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes a GTFetchHeadEntry. Designated initializer. +/// +/// reference - Reference on the repository. Cannot be nil. +/// remoteURLString - URL String where this was originally fetched from. Cannot be nil. +/// targetOID - Target OID. Cannot be nil. +/// merge - Indicates if this is pending a merge. +/// +/// Returns an initialized fetch head entry, or nil if an error occurred. +- (instancetype _Nullable)initWithReference:(GTReference *)reference remoteURLString:(NSString *)remoteURLString targetOID:(GTOID *)targetOID isMerge:(BOOL)merge NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTFetchHeadEntry.m b/ObjectiveGit/GTFetchHeadEntry.m new file mode 100644 index 000000000..ac8e1d235 --- /dev/null +++ b/ObjectiveGit/GTFetchHeadEntry.m @@ -0,0 +1,41 @@ +// +// GTFetchHeadEntry.m +// ObjectiveGitFramework +// +// Created by Pablo Bendersky on 8/14/14. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +#import "GTFetchHeadEntry.h" +#import "GTOID.h" + +@implementation GTFetchHeadEntry + +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithReference:(GTReference *)reference remoteURLString:(NSString *)remoteURLString targetOID:(GTOID *)targetOID isMerge:(BOOL)merge { + NSParameterAssert(reference != nil); + NSParameterAssert(remoteURLString != nil); + NSParameterAssert(targetOID != nil); + + self = [super init]; + if (self == nil) return nil; + + _reference = reference; + _remoteURLString = [remoteURLString copy]; + _targetOID = [targetOID copy]; + _merge = merge; + + return self; +} + +#pragma mark NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p>{ reference: %@, remoteURL: %@, targetOID: %@, merge: %i }", self.class, self, self.reference, self.remoteURLString, self.targetOID, (int)self.merge]; +} + +@end diff --git a/Classes/GTFilter.h b/ObjectiveGit/GTFilter.h similarity index 75% rename from Classes/GTFilter.h rename to ObjectiveGit/GTFilter.h index 0ad33e7e9..51b2e7196 100644 --- a/Classes/GTFilter.h +++ b/ObjectiveGit/GTFilter.h @@ -7,12 +7,12 @@ // #import -#import "git2.h" -#import "git2/sys/filter.h" @class GTRepository; @class GTFilterSource; +NS_ASSUME_NONNULL_BEGIN + /// The error domain for errors originating from GTFilter. extern NSString * const GTFilterErrorDomain; @@ -34,28 +34,32 @@ extern const NSInteger GTFilterErrorNameAlreadyRegistered; /// The check block. Determines whether the `applyBlock` should be run for given /// source. -@property (nonatomic, copy) BOOL (^checkBlock)(void **payload, GTFilterSource *source, const char **attr_values); +@property (nonatomic, copy) BOOL (^checkBlock)(void * _Null_unspecified * _Null_unspecified payload, GTFilterSource *source, const char * _Null_unspecified * _Null_unspecified attr_values); /// The cleanup block. Called after the `applyBlock` to given the filter a /// chance to clean up the `payload`. @property (nonatomic, copy) void (^cleanupBlock)(void *payload); -/// Initializes the object with the given name and attributes. +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the object with the given name and attributes. Designated initializer. /// /// name - The name for the filter. Cannot be nil. /// attributes - The attributes to which this filter applies. May be nil. /// applyBlock - The block to use to apply the filter. Cannot be nil. /// /// Returns the initialized object. -- (id)initWithName:(NSString *)name attributes:(NSString *)attributes applyBlock:(NSData * (^)(void **payload, NSData *from, GTFilterSource *source, BOOL *applied))applyBlock; +- (instancetype _Nullable)initWithName:(NSString *)name attributes:(NSString * _Nullable)attributes applyBlock:(NSData * (^)(void * _Null_unspecified * _Null_unspecified payload, NSData *from, GTFilterSource *source, BOOL *applied))applyBlock NS_DESIGNATED_INITIALIZER; /// Look up a filter based on its name. /// /// Note that this will only find filters registered through /// -registerWithName:priority:error:. /// +/// name - The name of the filter to retrieve. Must not be nil. +/// /// Returns the filter, or nil if none was found. -+ (GTFilter *)filterForName:(NSString *)name; ++ (GTFilter * _Nullable)filterForName:(NSString *)name; /// Registers the filter with the given priority. /// @@ -75,3 +79,5 @@ extern const NSInteger GTFilterErrorNameAlreadyRegistered; - (BOOL)unregister:(NSError **)error; @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTFilter.m b/ObjectiveGit/GTFilter.m similarity index 91% rename from Classes/GTFilter.m rename to ObjectiveGit/GTFilter.m index aabfcbf76..69c185ca7 100644 --- a/Classes/GTFilter.m +++ b/ObjectiveGit/GTFilter.m @@ -10,6 +10,8 @@ #import "GTRepository.h" #import "NSError+Git.h" #import "GTFilterSource.h" + +#import "git2/errors.h" #import "git2/sys/filter.h" NSString * const GTFilterErrorDomain = @"GTFilterErrorDomain"; @@ -40,7 +42,12 @@ + (void)initialize { GTFiltersGitFilterToRegisteredFilters = [[NSMutableDictionary alloc] init]; } -- (id)initWithName:(NSString *)name attributes:(NSString *)attributes applyBlock:(NSData * (^)(void **payload, NSData *from, GTFilterSource *source, BOOL *applied))applyBlock { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithName:(NSString *)name attributes:(NSString *)attributes applyBlock:(NSData * (^)(void **payload, NSData *from, GTFilterSource *source, BOOL *applied))applyBlock { NSParameterAssert(name != nil); NSParameterAssert(applyBlock != NULL); @@ -89,7 +96,9 @@ static void GTFilterShutdown(git_filter *filter) { static int GTFilterCheck(git_filter *filter, void **payload, const git_filter_source *src, const char **attr_values) { GTFilter *self = GTFiltersGitFilterToRegisteredFilters[[NSValue valueWithPointer:filter]]; - BOOL accept = self.checkBlock(payload, [[GTFilterSource alloc] initWithGitFilterSource:src], attr_values); + GTFilterSource *source = [[GTFilterSource alloc] initWithGitFilterSource:src]; + NSCAssert(source != nil, @"Unexpected nil filter source"); + BOOL accept = self.checkBlock(payload, source, attr_values); return accept ? 0 : GIT_PASSTHROUGH; } diff --git a/Classes/GTFilterList.h b/ObjectiveGit/GTFilterList.h similarity index 69% rename from Classes/GTFilterList.h rename to ObjectiveGit/GTFilterList.h index 023d04d64..6ddf3ca98 100644 --- a/Classes/GTFilterList.h +++ b/ObjectiveGit/GTFilterList.h @@ -7,26 +7,32 @@ // #import -#import "git2.h" +#import "git2/filter.h" @class GTBlob; @class GTRepository; /// The options for loading a filter list. See libgit2 for more information. -typedef enum { - GTFilterListOptionsDefault = GIT_FILTER_OPT_DEFAULT, - GTFilterListOptionsAllowUnsafe = GIT_FILTER_OPT_ALLOW_UNSAFE, -} GTFilterListOptions; +typedef NS_OPTIONS(NSInteger, GTFilterListOptions) { + GTFilterListOptionsDefault = GIT_FILTER_DEFAULT, + GTFilterListOptionsAllowUnsafe = GIT_FILTER_ALLOW_UNSAFE, +}; + +NS_ASSUME_NONNULL_BEGIN /// An opaque list of filters that apply to a given path. @interface GTFilterList : NSObject -/// Initializes the receiver to wrap the given `git_filter_list`. +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver to wrap the given `git_filter_list`. Designated initializer. /// /// filterList - The filter list to wrap and take ownership of. This filter list /// will be automatically disposed when the receiver deallocates. /// Must not be NULL. -- (instancetype)initWithGitFilterList:(git_filter_list *)filterList; +/// +/// Returns an initialized filter list, or nil if an error occurred. +- (instancetype _Nullable)initWithGitFilterList:(git_filter_list *)filterList NS_DESIGNATED_INITIALIZER; /// Returns the underlying `git_filter_list`. - (git_filter_list *)git_filter_list __attribute__((objc_returns_inner_pointer)); @@ -37,7 +43,7 @@ typedef enum { /// error - If not NULL, set to any error that occurs. /// /// Returns the filtered data, or nil if an error occurs. -- (NSData *)applyToData:(NSData *)inputData error:(NSError **)error; +- (NSData * _Nullable)applyToData:(NSData *)inputData error:(NSError **)error; /// Attempts to apply the filter list to a file in the given repository. /// @@ -47,7 +53,7 @@ typedef enum { /// error - If not NULL, set to any error that occurs. /// /// Returns the filtered data, or nil if an error occurs. -- (NSData *)applyToPath:(NSString *)relativePath inRepository:(GTRepository *)repository error:(NSError **)error; +- (NSData * _Nullable)applyToPath:(NSString *)relativePath inRepository:(GTRepository *)repository error:(NSError **)error; /// Attempts to apply the filter list to a blob. /// @@ -55,6 +61,8 @@ typedef enum { /// error - If not NULL, set to any error that occurs. /// /// Returns the filtered data, or nil if an error occurs. -- (NSData *)applyToBlob:(GTBlob *)blob error:(NSError **)error; +- (NSData * _Nullable)applyToBlob:(GTBlob *)blob error:(NSError **)error; @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTFilterList.m b/ObjectiveGit/GTFilterList.m similarity index 74% rename from Classes/GTFilterList.m rename to ObjectiveGit/GTFilterList.m index e007f5a64..1780293a4 100644 --- a/Classes/GTFilterList.m +++ b/ObjectiveGit/GTFilterList.m @@ -10,8 +10,11 @@ #import "GTBlob.h" #import "GTRepository.h" +#import "NSData+Git.h" #import "NSError+Git.h" +#import "git2/errors.h" + @interface GTFilterList () @property (nonatomic, assign, readonly) git_filter_list *git_filter_list; @@ -22,6 +25,11 @@ @implementation GTFilterList #pragma mark Lifecycle +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitFilterList:(git_filter_list *)filterList { NSParameterAssert(filterList != NULL); @@ -45,24 +53,16 @@ - (void)dealloc { - (NSData *)applyToData:(NSData *)inputData error:(NSError **)error { NSParameterAssert(inputData != nil); - git_buf input = (git_buf){ - .ptr = (void *)inputData.bytes, - .asize = 0, - .size = inputData.length, - }; - + git_buf input = inputData.git_buf; git_buf output = GIT_BUF_INIT_CONST(0, NULL); int gitError = git_filter_list_apply_to_data(&output, self.git_filter_list, &input); + if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to apply filter list to data buffer"]; return nil; } - // TODO: Reuse output buffers if possible. - NSData *data = [[NSData alloc] initWithBytes:output.ptr length:output.size]; - git_buf_free(&output); - - return data; + return [NSData git_dataWithBuffer:&output]; } - (NSData *)applyToPath:(NSString *)relativePath inRepository:(GTRepository *)repository error:(NSError **)error { @@ -70,17 +70,18 @@ - (NSData *)applyToPath:(NSString *)relativePath inRepository:(GTRepository *)re NSParameterAssert(repository != nil); git_buf output = GIT_BUF_INIT_CONST(0, NULL); - int gitError = git_filter_list_apply_to_file(&output, self.git_filter_list, repository.git_repository, relativePath.fileSystemRepresentation); + // fixme: This is a workaround for an issue where `git_filter_list_apply_to_file` + // will not resolve relative paths against the worktree. It should be reverted when + // libgit2 has been updated to resolve that. + NSString *absolutePath = relativePath.absolutePath ? relativePath : [repository.fileURL URLByAppendingPathComponent:relativePath].path; + int gitError = git_filter_list_apply_to_file(&output, self.git_filter_list, repository.git_repository, absolutePath.UTF8String); + if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to apply filter list to %@", relativePath]; return nil; } - // TODO: Reuse output buffers if possible. - NSData *data = [[NSData alloc] initWithBytes:output.ptr length:output.size]; - git_buf_free(&output); - - return data; + return [NSData git_dataWithBuffer:&output]; } - (NSData *)applyToBlob:(GTBlob *)blob error:(NSError **)error { @@ -88,16 +89,13 @@ - (NSData *)applyToBlob:(GTBlob *)blob error:(NSError **)error { git_buf output = GIT_BUF_INIT_CONST(0, NULL); int gitError = git_filter_list_apply_to_blob(&output, self.git_filter_list, blob.git_blob); + if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to apply filter list to blob %@", blob.OID]; return nil; } - // TODO: Reuse output buffers if possible. - NSData *data = [[NSData alloc] initWithBytes:output.ptr length:output.size]; - git_buf_free(&output); - - return data; + return [NSData git_dataWithBuffer:&output]; } @end diff --git a/Classes/GTFilterSource.h b/ObjectiveGit/GTFilterSource.h similarity index 74% rename from Classes/GTFilterSource.h rename to ObjectiveGit/GTFilterSource.h index acd470a07..e168846d0 100644 --- a/Classes/GTFilterSource.h +++ b/ObjectiveGit/GTFilterSource.h @@ -7,7 +7,6 @@ // #import -#import "git2.h" #import "git2/sys/filter.h" @class GTOID; @@ -18,10 +17,12 @@ /// GTFilterSourceModeSmudge - Performed when the source is going into the work /// tree. /// GTFilterSourceModeClean - Performed when the source is going into the ODB. -typedef enum { +typedef NS_ENUM(NSInteger, GTFilterSourceMode) { GTFilterSourceModeSmudge = GIT_FILTER_SMUDGE, GTFilterSourceModeClean = GIT_FILTER_CLEAN, -} GTFilterSourceMode; +}; + +NS_ASSUME_NONNULL_BEGIN /// A source item for a filter. @interface GTFilterSource : NSObject @@ -34,16 +35,20 @@ typedef enum { /// The OID of the source. Will be nil if the source doesn't exist in the object /// database. -@property (nonatomic, readonly, strong) GTOID *OID; +@property (nonatomic, readonly, strong) GTOID * _Nullable OID; /// The filter mode. @property (nonatomic, readonly, assign) GTFilterSourceMode mode; -/// Intializes the receiver with the given filter source. +- (instancetype)init NS_UNAVAILABLE; + +/// Intializes the receiver with the given filter source. Designated initializer. /// /// source - The filter source. Cannot be NULL. /// /// Returns the initialized object. -- (id)initWithGitFilterSource:(const git_filter_source *)source; +- (instancetype _Nullable)initWithGitFilterSource:(const git_filter_source *)source NS_DESIGNATED_INITIALIZER; @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTFilterSource.m b/ObjectiveGit/GTFilterSource.m similarity index 60% rename from Classes/GTFilterSource.m rename to ObjectiveGit/GTFilterSource.m index 07a2df69e..65878319f 100644 --- a/Classes/GTFilterSource.m +++ b/ObjectiveGit/GTFilterSource.m @@ -10,20 +10,30 @@ #import "GTRepository.h" #import "GTOID.h" +#import "git2/repository.h" + @implementation GTFilterSource #pragma mark Lifecycle -- (id)initWithGitFilterSource:(const git_filter_source *)source { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithGitFilterSource:(const git_filter_source *)source { NSParameterAssert(source != NULL); self = [super init]; if (self == nil) return nil; - const char *path = git_repository_workdir(git_filter_source_repo(source)); - _repositoryURL = [NSURL fileURLWithPath:@(path)]; - - _path = @(git_filter_source_path(source)); + NSString *path = @(git_repository_workdir(git_filter_source_repo(source))); + NSAssert(path, @"workdir was nil"); + _repositoryURL = [NSURL fileURLWithPath:path]; + + path = @(git_filter_source_path(source)); + NSAssert(path, @"path was nil"); + _path = path; const git_oid *gitOid = git_filter_source_id(source); if (gitOid != NULL) _OID = [[GTOID alloc] initWithGitOid:gitOid]; diff --git a/ObjectiveGit/GTIndex.h b/ObjectiveGit/GTIndex.h new file mode 100644 index 000000000..d0df6e4ec --- /dev/null +++ b/ObjectiveGit/GTIndex.h @@ -0,0 +1,239 @@ +// +// GTIndex.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 2/28/11. +// +// The MIT License +// +// Copyright (c) 2011 Tim Clem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#include "git2/types.h" + +@class GTIndexEntry; +@class GTRepository; +@class GTTree; + +NS_ASSUME_NONNULL_BEGIN + +@interface GTIndex : NSObject + +/// The repository in which the index resides. This may be nil if the index was +/// created with -initWithFileURL:error:. +@property (nonatomic, readonly, strong) GTRepository * _Nullable repository; + +/// The file URL for the index if it exists on disk; nil otherwise. +@property (nonatomic, readonly, copy) NSURL * _Nullable fileURL; + +/// The number of entries in the index. +@property (nonatomic, readonly) NSUInteger entryCount; + +/// The GTIndexEntries in the index. +@property (nonatomic, readonly, copy) NSArray *entries; + +/// Whether the index contains conflicted files. +@property (nonatomic, readonly) BOOL hasConflicts; + +/// Creates an in-memory index. +/// +/// repository - A repository that paths should be relative to. Cannot be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the newly created index, or nil if an error occurred. ++ (instancetype _Nullable)inMemoryIndexWithRepository:(GTRepository *)repository error:(NSError **)error; + +/// Loads the index at the given file URL. +/// +/// fileURL - The file URL for the index on disk. Cannot be nil. +/// repository - A repository that paths should be relative to. Cannot be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the loaded index, or nil if an error occurred. ++ (instancetype _Nullable)indexWithFileURL:(NSURL *)fileURL repository:(GTRepository *)repository error:(NSError **)error; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver with the given libgit2 index. Designated initializer. +/// +/// index - The libgit2 index from which the index should be created. Cannot +/// be NULL. +/// repository - The repository in which the index resides. Cannot be nil. +/// +/// Returns the initialized index. +- (instancetype)initWithGitIndex:(git_index *)index repository:(GTRepository *)repository NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_index` object. +- (git_index *)git_index __attribute__((objc_returns_inner_pointer)); + +/// Refresh the index from the datastore +/// +/// error - The error if one occurred. +/// +/// Returns whether the refresh was successful. +- (BOOL)refresh:(NSError **)error; + +/// Clear all the entries from the index. This happens in memory. Changes can be +/// written to the datastore by calling -write:. +/// +/// error - The error if one ocurred. +/// +/// Returns whether the clear operation was successful. +- (BOOL)clear:(NSError **)error; + +/// Get the entry at the given index. +/// +/// index - The index of the entry to get. Must be within 0 and self.entryCount. +/// +/// Returns a new GTIndexEntry, or nil if an error occurred. +- (GTIndexEntry * _Nullable)entryAtIndex:(NSUInteger)index; + +/// Get the entry with the given path, or nil if an error occurred. +- (GTIndexEntry * _Nullable)entryWithPath:(NSString *)path; + +/// Get the entry with the given name. +/// +/// path - The path of the entry to get. Cannot be nil. +/// error - The error if one occurred. +/// +/// Returns a new GTIndexEntry, or nil if an error occurred. +- (GTIndexEntry * _Nullable)entryWithPath:(NSString *)path error:(NSError **)error; + +/// Add an entry to the index. +/// +/// Note that this *cannot* add submodules. See -[GTSubmodule addToIndex:]. +/// +/// entry - The entry to add. Must not be nil. +/// error - The error if one occurred. +/// +/// Returns YES if successful, NO otherwise. +- (BOOL)addEntry:(GTIndexEntry *)entry error:(NSError **)error; + +/// Add an entry (by relative path) to the index. +/// Will fail if the receiver's repository is nil. +/// +/// Note that this *cannot* add submodules. See -[GTSubmodule addToIndex:]. +/// +/// file - The path (relative to the root of the repository) of the file to add. +/// error - The error if one occurred. +/// +/// Returns YES if successful, NO otherwise. +- (BOOL)addFile:(NSString *)file error:(NSError **)error; + +/// Add an entry (with the provided data and name) to the index. +/// Will fail if the receiver's repository is nil. +/// +/// data - The content of the entry to add. Cannot be nil. +/// path - The path of the entry to add. Cannot be nil. +/// error - The error if one occurred. +- (BOOL)addData:(NSData *)data withPath:(NSString *)path error:(NSError **)error; + +/// Reads the contents of the given tree into the index. +/// +/// tree - The tree to add to the index. This must not be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns whether reading the tree was successful. +- (BOOL)addContentsOfTree:(GTTree *)tree error:(NSError **)error; + +/// Add all the content of the working directory to the index. Like `git add -A` +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns whether the operation was successful +- (BOOL)addAll:(NSError **)error; + +/// Remove an entry (by relative path) from the index. +/// Will fail if the receiver's repository is nil. +/// +/// file - The path (relative to the root of the repository) of the file to +/// remove. +/// error - The error, if one occurred. +/// +/// Returns YES if successful, NO otherwise. +- (BOOL)removeFile:(NSString *)file error:(NSError **)error; + +/// Write the index to the repository. +/// Will fail if the receiver's repository is nil. +/// +/// error - The error if one occurred. +/// +/// Returns YES if successful, NO otherwise. +- (BOOL)write:(NSError **)error; + +/// Write the index to the repository as a tree. +/// Will fail if the receiver's repository is nil. +/// +/// error - The error if one occurred. +/// +/// Returns a new GTTree, or nil if an error occurred. +- (GTTree * _Nullable)writeTree:(NSError **)error; + +/// Write the index to the given repository as a tree. +/// Will fail if the receiver's index has conflicts. +/// +/// repository - The repository to write the tree to. Can't be nil. +/// error - The error if one occurred. +/// +/// Returns a new GTTree or nil if an error occurred. +- (GTTree * _Nullable)writeTreeToRepository:(GTRepository *)repository error:(NSError **)error; + +/// Enumerate through any conflicts in the index, running the provided block each +/// time. +/// +/// error - Optionally set in the event of failure. +/// block - A block to be run on each conflicted entry. Passed in are index +/// entries which represent the common ancestor as well as our and their +/// side of the conflict. If the block sets `stop` to YES then the +/// iteration will cease once the current block execution has finished. +/// Must not be nil. +/// +/// Returns `YES` in the event of successful enumeration or no conflicts in the +/// index, `NO` in case of error. +- (BOOL)enumerateConflictedFilesWithError:(NSError **)error usingBlock:(void (^)(GTIndexEntry *ancestor, GTIndexEntry *ours, GTIndexEntry *theirs, BOOL *stop))block; + +/// Update all index entries to match the working directory. +/// This method will immediately fail if the index's repo is bare. +/// +/// pathspecs - An `NSString` array of path patterns. (E.g: *.c) +/// If nil is passed in, all index entries will be updated. +/// block - A block run each time a pathspec is matched; before the index is +/// updated. The `matchedPathspec` parameter is a string indicating +/// what the pathspec (from `pathspecs`) matched. If you pass in NULL +/// in to the `pathspecs` parameter this parameter will be empty. +/// The `path` parameter is a repository relative path to the file +/// about to be updated. +/// The `stop` parameter can be set to `YES` to abort the operation. +/// Return `YES` to update the given path, or `NO` to skip it. May be nil. +/// error - When something goes wrong, this parameter is set. Optional. +/// +/// Returns `YES` in the event that everything has gone smoothly. Otherwise, `NO`. +- (BOOL)updatePathspecs:(NSArray * _Nullable)pathspecs error:(NSError **)error passingTest:(BOOL (^ _Nullable)(NSString *matchedPathspec, NSString *path, BOOL *stop))block; + +#pragma mark Deprecations +- (GTIndexEntry * _Nullable)entryWithName:(NSString *)name __deprecated_msg("use entryWithPath: instead."); +- (GTIndexEntry * _Nullable)entryWithName:(NSString *)name error:(NSError **)error __deprecated_msg("use entryWithPath:error: instead."); + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTIndex.m b/ObjectiveGit/GTIndex.m similarity index 84% rename from Classes/GTIndex.m rename to ObjectiveGit/GTIndex.m index d6c61fafb..8ee64fde6 100644 --- a/Classes/GTIndex.m +++ b/ObjectiveGit/GTIndex.m @@ -28,14 +28,19 @@ // #import "GTIndex.h" -#import "GTIndexEntry.h" -#import "NSError+Git.h" -#import "GTRepository.h" -#import "GTRepository+Private.h" + +#import "EXTScope.h" #import "GTConfiguration.h" +#import "GTIndexEntry.h" #import "GTOID.h" +#import "GTRepository+Private.h" +#import "GTRepository.h" #import "GTTree.h" -#import "EXTScope.h" +#import "GTBlob.h" +#import "NSArray+StringArray.h" +#import "NSError+Git.h" + +#import "git2/errors.h" // The block synonymous with libgit2's `git_index_matched_path_cb` callback. typedef BOOL (^GTIndexPathspecMatchedBlock)(NSString *matchedPathspec, NSString *path, BOOL *stop); @@ -72,6 +77,7 @@ - (void)dealloc { + (instancetype)inMemoryIndexWithRepository:(GTRepository *)repository error:(NSError **)error { git_index *index = NULL; + int status = git_index_new(&index); if (status != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to initialize in-memory index"]; @@ -81,6 +87,11 @@ + (instancetype)inMemoryIndexWithRepository:(GTRepository *)repository error:(NS return [[self alloc] initWithGitIndex:index repository:repository]; } +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + + (instancetype)indexWithFileURL:(NSURL *)fileURL repository:(GTRepository *)repository error:(NSError **)error { NSParameterAssert(fileURL != nil); NSParameterAssert(fileURL.isFileURL); @@ -124,26 +135,31 @@ - (BOOL)refresh:(NSError **)error { return YES; } -- (void)clear { - git_index_clear(self.git_index); +- (BOOL)clear:(NSError **)error { + int gitError = git_index_clear(self.git_index); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to clear index"]; + return NO; + } + return YES; } - (GTIndexEntry *)entryAtIndex:(NSUInteger)index { const git_index_entry *entry = git_index_get_byindex(self.git_index, (unsigned int)index); if (entry == NULL) return nil; - return [[GTIndexEntry alloc] initWithGitIndexEntry:entry]; + return [[GTIndexEntry alloc] initWithGitIndexEntry:entry index:self error:NULL]; } -- (GTIndexEntry *)entryWithName:(NSString *)name { - return [self entryWithName:name error:NULL]; +- (GTIndexEntry *)entryWithPath:(NSString *)path { + return [self entryWithPath:path error:NULL]; } -- (GTIndexEntry *)entryWithName:(NSString *)name error:(NSError **)error { +- (GTIndexEntry *)entryWithPath:(NSString *)path error:(NSError **)error { size_t pos = 0; - int gitError = git_index_find(&pos, self.git_index, name.UTF8String); + int gitError = git_index_find(&pos, self.git_index, path.UTF8String); if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"%@ not found in index", name]; + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"%@ not found in index", path]; return NULL; } return [self entryAtIndex:pos]; @@ -171,6 +187,25 @@ - (BOOL)addFile:(NSString *)file error:(NSError **)error { return YES; } +- (BOOL)addData:(NSData *)data withPath:(NSString *)path error:(NSError **)error { + NSParameterAssert(data != nil); + NSParameterAssert(path != nil); + + git_index_entry entry; + memset(&entry, 0x0, sizeof(git_index_entry)); + entry.path = [path cStringUsingEncoding:NSUTF8StringEncoding]; + entry.mode = GIT_FILEMODE_BLOB; + + int status = git_index_add_frombuffer(self.git_index, &entry, [data bytes], [data length]); + + if (status != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to add data with name %@ into index.", path]; + return NO; + } + + return YES; +} + - (BOOL)addContentsOfTree:(GTTree *)tree error:(NSError **)error { NSParameterAssert(tree != nil); @@ -183,6 +218,16 @@ - (BOOL)addContentsOfTree:(GTTree *)tree error:(NSError **)error { return YES; } +- (BOOL)addAll:(NSError **)error { + int status = git_index_add_all(self.git_index, nil, GIT_INDEX_ADD_CHECK_PATHSPEC, nil, nil); + if (status != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to add all the contents of the working tree to the index"]; + return NO; + } + + return YES; +} + - (BOOL)removeFile:(NSString *)file error:(NSError **)error { NSString *unicodeString = [self composedUnicodeStringWithString:file]; @@ -191,7 +236,7 @@ - (BOOL)removeFile:(NSString *)file error:(NSError **)error { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to remove file %@ from index.", file]; return NO; } - + return YES; } @@ -207,7 +252,7 @@ - (BOOL)write:(NSError **)error { - (GTTree *)writeTree:(NSError **)error { git_oid oid; - + int status = git_index_write_tree(&oid, self.git_index); if (status != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to write index."]; @@ -220,20 +265,22 @@ - (GTTree *)writeTree:(NSError **)error { - (GTTree *)writeTreeToRepository:(GTRepository *)repository error:(NSError **)error { NSParameterAssert(repository != nil); git_oid oid; - + int status = git_index_write_tree_to(&oid, self.git_index, repository.git_repository); if (status != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to write index to repository %@", repository]; return NULL; } - + return [repository lookUpObjectByGitOid:&oid objectType:GTObjectTypeTree error:NULL]; } - (NSArray *)entries { NSMutableArray *entries = [NSMutableArray arrayWithCapacity:self.entryCount]; for (NSUInteger i = 0; i < self.entryCount; i++) { - [entries addObject:[self entryAtIndex:i]]; + GTIndexEntry *entry = [self entryAtIndex:i]; + if (entry) + [entries addObject:entry]; } return entries; @@ -248,31 +295,31 @@ - (BOOL)hasConflicts { - (BOOL)enumerateConflictedFilesWithError:(NSError **)error usingBlock:(void (^)(GTIndexEntry *ancestor, GTIndexEntry *ours, GTIndexEntry *theirs, BOOL *stop))block { NSParameterAssert(block != nil); if (!self.hasConflicts) return YES; - + git_index_conflict_iterator *iterator = NULL; int returnCode = git_index_conflict_iterator_new(&iterator, self.git_index); if (returnCode != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:returnCode description:NSLocalizedString(@"Could not create git index iterator.", nil)]; return NO; } - + @onExit { if (iterator != NULL) git_index_conflict_iterator_free(iterator); }; - + while (YES) { const git_index_entry *ancestor = NULL; const git_index_entry *ours = NULL; const git_index_entry *theirs = NULL; - + returnCode = git_index_conflict_next(&ancestor, &ours, &theirs, iterator); if (returnCode == GIT_ITEROVER) break; - + if (returnCode != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:returnCode description:NSLocalizedString(@"Could not iterate conflict.", nil)]; return NO; } - + GTIndexEntry *blockAncestor; if (ancestor != NULL) { blockAncestor = [[GTIndexEntry alloc] initWithGitIndexEntry:ancestor]; @@ -292,7 +339,7 @@ - (BOOL)enumerateConflictedFilesWithError:(NSError **)error usingBlock:(void (^) block(blockAncestor, blockOurs, blockTheirs, &stop); if (stop) break; } - + return YES; } @@ -303,7 +350,7 @@ - (BOOL)enumerateConflictedFilesWithError:(NSError **)error usingBlock:(void (^) - (BOOL)updatePathspecs:(NSArray *)pathspecs error:(NSError **)error passingTest:(GTIndexPathspecMatchedBlock)block { NSAssert(self.repository.isBare == NO, @"This method only works with non-bare repositories."); - + const git_strarray strarray = pathspecs.git_strarray; struct GTIndexPathspecMatchedInfo payload = { .block = block, @@ -315,7 +362,7 @@ - (BOOL)updatePathspecs:(NSArray *)pathspecs error:(NSError **)error passingTest if (error != nil) *error = [NSError git_errorFor:returnCode description:NSLocalizedString(@"Could not update index.", nil)]; return NO; } - + return YES; } @@ -325,11 +372,11 @@ int GTIndexPathspecMatchFound(const char *path, const char *matched_pathspec, vo if (info->shouldAbortImmediately) { return GIT_EUSER; } - + BOOL shouldStop = NO; NSString *matchedPathspec = (matched_pathspec != nil ? @(matched_pathspec): nil); BOOL shouldUpdate = block(matchedPathspec, @(path), &shouldStop); - + if (shouldUpdate) { if (shouldStop) { info->shouldAbortImmediately = YES; @@ -350,4 +397,13 @@ - (NSString *)composedUnicodeStringWithString:(NSString *)string { return (shouldPrecompose ? [string precomposedStringWithCanonicalMapping] : [string decomposedStringWithCanonicalMapping]); } +#pragma mark Deprecations + +- (GTIndexEntry *)entryWithName:(NSString *)name { + return [self entryWithPath:name]; +} + +- (GTIndexEntry *)entryWithName:(NSString *)name error:(NSError **)error { + return [self entryWithPath:name error:error]; +} @end diff --git a/Classes/GTIndexEntry.h b/ObjectiveGit/GTIndexEntry.h similarity index 52% rename from Classes/GTIndexEntry.h rename to ObjectiveGit/GTIndexEntry.h index df3458ac7..16b89a588 100644 --- a/Classes/GTIndexEntry.h +++ b/ObjectiveGit/GTIndexEntry.h @@ -27,35 +27,68 @@ // THE SOFTWARE. // -#include "git2.h" +#import +#include "git2/index.h" +#import "GTObject.h" -typedef enum { +@class GTIndex; + +typedef NS_ENUM(NSInteger, GTIndexEntryStatus) { GTIndexEntryStatusUpdated = 0, GTIndexEntryStatusConflicted, GTIndexEntryStatusAdded, GTIndexEntryStatusRemoved, GTIndexEntryStatusUpToDate, -} GTIndexEntryStatus; +}; + +NS_ASSUME_NONNULL_BEGIN @interface GTIndexEntry : NSObject -// The repository-relative path for the entry. +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver with the given libgit2 index entry. +/// +/// entry - The libgit2 index entry. Cannot be NULL. +/// index - The index this entry belongs to. +/// error - will be filled if an error occurs +/// +/// Returns the initialized object. +- (instancetype)initWithGitIndexEntry:(const git_index_entry *)entry index:(GTIndex * _Nullable)index error:(NSError **)error NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithGitIndexEntry:(const git_index_entry *)entry; + +/// The underlying `git_index_entry` object. +- (const git_index_entry *)git_index_entry __attribute__((objc_returns_inner_pointer)); + +/// The entry's index. This may be nil if nil is passed in to -initWithGitIndexEntry: +@property (nonatomic, strong, readonly) GTIndex * _Nullable index; + +/// The repository-relative path for the entry. @property (nonatomic, readonly, copy) NSString *path; -// Has the entry been staged? +/// Has the entry been staged? @property (nonatomic, getter = isStaged, readonly) BOOL staged; -// What is the entry's status? +/// What is the entry's status? @property (nonatomic, readonly) GTIndexEntryStatus status; -// Initializes the receiver with the given libgit2 index entry. -// -// entry - The libgit2 index entry. Cannot be NULL. -// -// Returns the initialized object. -- (id)initWithGitIndexEntry:(const git_index_entry *)entry; +/// The OID of the entry. +@property (nonatomic, strong, readonly) GTOID *OID; -// The underlying `git_index_entry` object. -- (const git_index_entry *)git_index_entry __attribute__((objc_returns_inner_pointer)); +/// Convert the entry into an GTObject +/// +/// error - will be filled if an error occurs +/// +/// Returns this entry as a GTObject or nil if an error occurred. +- (nullable GTObject *)GTObject:(NSError **)error; @end + +@interface GTObject (GTIndexEntry) + ++ (instancetype _Nullable)objectWithIndexEntry:(GTIndexEntry *)indexEntry error:(NSError **)error; +- (instancetype _Nullable)initWithIndexEntry:(GTIndexEntry *)indexEntry error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTIndexEntry.m b/ObjectiveGit/GTIndexEntry.m similarity index 53% rename from Classes/GTIndexEntry.m rename to ObjectiveGit/GTIndexEntry.m index 0360755da..c687d0060 100644 --- a/Classes/GTIndexEntry.m +++ b/ObjectiveGit/GTIndexEntry.m @@ -30,6 +30,11 @@ #import "GTIndexEntry.h" #import "NSError+Git.h" #import "NSString+Git.h" +#import "GTOID.h" +#import "GTRepository.h" +#import "GTIndex.h" + +#import "git2.h" @interface GTIndexEntry () @property (nonatomic, assign, readonly) const git_index_entry *git_index_entry; @@ -40,26 +45,38 @@ @implementation GTIndexEntry #pragma mark NSObject - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p> path: %@", self.class, self, self.path]; + return [NSString stringWithFormat:@"<%@: %p> path: %@", self.class, self, self.path]; } #pragma mark Lifecycle -- (id)initWithGitIndexEntry:(const git_index_entry *)entry { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithGitIndexEntry:(const git_index_entry *)entry index:(GTIndex *)index error:(NSError **)error { NSParameterAssert(entry != NULL); self = [super init]; if (self == nil) return nil; _git_index_entry = entry; - + _index = index; + return self; } +- (instancetype)initWithGitIndexEntry:(const git_index_entry *)entry { + return [self initWithGitIndexEntry:entry index:nil error:NULL]; +} + #pragma mark Properties - (NSString *)path { - return @(self.git_index_entry->path); + NSString *path = @(self.git_index_entry->path); + NSAssert(path, @"path is nil"); + return path; } - (int)flags { @@ -71,19 +88,56 @@ - (BOOL)isStaged { } - (GTIndexEntryStatus)status { - if ((self.flags & GIT_IDXENTRY_UPDATE) != 0) { + if ((self.flags & (GIT_IDXENTRY_UPDATE << 16)) != 0) { return GTIndexEntryStatusUpdated; - } else if ((self.flags & GIT_IDXENTRY_UPTODATE) != 0) { + } else if ((self.flags & (GIT_IDXENTRY_UPTODATE << 16)) != 0) { return GTIndexEntryStatusUpToDate; - } else if ((self.flags & GIT_IDXENTRY_CONFLICTED) != 0) { + } else if ((self.flags & (GIT_IDXENTRY_CONFLICTED << 16)) != 0) { return GTIndexEntryStatusConflicted; - } else if ((self.flags & GIT_IDXENTRY_ADDED) != 0) { + } else if ((self.flags & (GIT_IDXENTRY_ADDED << 16)) != 0) { return GTIndexEntryStatusAdded; - } else if ((self.flags & GIT_IDXENTRY_REMOVE) != 0) { + } else if ((self.flags & (GIT_IDXENTRY_REMOVE << 16)) != 0) { return GTIndexEntryStatusRemoved; } - + return GTIndexEntryStatusUpToDate; } +- (GTOID *)OID { + return [GTOID oidWithGitOid:&self.git_index_entry->id]; +} + +#pragma mark API + +- (GTRepository *)repository { + return self.index.repository; +} + +- (GTObject *)GTObject:(NSError **)error { + return [GTObject objectWithIndexEntry:self error:error]; +} + +@end + +@implementation GTObject (GTIndexEntry) + ++ (instancetype)objectWithIndexEntry:(GTIndexEntry *)indexEntry error:(NSError **)error { + return [[self alloc] initWithIndexEntry:indexEntry error:error]; +} + +- (instancetype)initWithIndexEntry:(GTIndexEntry *)indexEntry error:(NSError **)error { + git_object *obj; + int gitError = git_object_lookup(&obj, indexEntry.repository.git_repository, indexEntry.OID.git_oid, (git_object_t)GTObjectTypeAny); + + if (gitError < GIT_OK) { + if (error != NULL) { + *error = [NSError git_errorFor:gitError description:@"Failed to get object for index entry."]; + } + + return nil; + } + + return [self initWithObj:obj inRepository:indexEntry.repository]; +} + @end diff --git a/ObjectiveGit/GTNote.h b/ObjectiveGit/GTNote.h new file mode 100644 index 000000000..008b78a5a --- /dev/null +++ b/ObjectiveGit/GTNote.h @@ -0,0 +1,91 @@ +// +// GTNote.h +// ObjectiveGitFramework +// +// Created by Slava Karpenko on 5/16/2016. +// +// The MIT License +// +// Copyright (c) 2016 Wildbit LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "git2/oid.h" + +@class GTSignature; +@class GTRepository; +@class GTOID; +@class GTObject; + +NS_ASSUME_NONNULL_BEGIN + +@interface GTNote : NSObject {} + +/// The author of the note. +@property (nonatomic, readonly, strong) GTSignature * _Nullable author; + +/// The committer of the note. +@property (nonatomic, readonly, strong) GTSignature * _Nullable committer; + +/// Content of the note. +@property (nonatomic, readonly, strong) NSString *note; + +@property (nonatomic, readonly, strong) GTOID *targetOID; + +/// The underlying `git_note` object. +- (git_note *)git_note __attribute__((objc_returns_inner_pointer)); + +/// Create a note with target OID in the given repository. +/// +/// oid - OID of the target to attach to +/// repository - Repository containing the target OID refers to +/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits") +/// error - Will be filled with a NSError object in case of error. +/// May be NULL. +/// +/// Returns initialized GTNote instance or nil on failure (error will be populated, if passed). +- (instancetype _Nullable)initWithTargetOID:(GTOID *)oid repository:(GTRepository *)repository referenceName:(NSString * _Nullable)referenceName error:(NSError **)error; + +/// Create a note with target libgit2 oid in the given repository. +/// +/// oid - git_oid of the target to attach to +/// repository - Repository containing the target OID refers to +/// referenceName - Name for the notes reference in the repo, or NULL for default ("refs/notes/commits") +/// +/// Returns initialized GTNote instance or nil on failure. +- (instancetype _Nullable)initWithTargetGitOID:(git_oid *)oid repository:(git_repository *)repository referenceName:(const char * _Nullable)referenceName error:(NSError **)error NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + + +/// Return a default reference name (that is used if you pass nil to any referenceName parameter) +/// +/// repository - Repository for which to get the default notes reference name. +/// error - Will be filled with a git error code in case of error. +/// May be NULL. +/// +/// Returns default reference name (usually "refs/notes/commits"). ++ (NSString * _Nullable)defaultReferenceNameForRepository:(GTRepository *)repository error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/ObjectiveGit/GTNote.m b/ObjectiveGit/GTNote.m new file mode 100644 index 000000000..b87039de1 --- /dev/null +++ b/ObjectiveGit/GTNote.m @@ -0,0 +1,105 @@ +// +// GTNote.m +// ObjectiveGitFramework +// +// Created by Slava Karpenko on 16.05.16. +// Copyright © 2016 Wildbit LLC. All rights reserved. +// + +#import "GTNote.h" +#import "NSError+Git.h" +#import "GTSignature.h" +#import "GTReference.h" +#import "GTRepository.h" +#import "NSString+Git.h" +#import "GTOID.h" + +#import "git2/errors.h" +#import "git2/notes.h" + +@interface GTNote () +{ + git_note *_note; +} + +@end +@implementation GTNote + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self]; +} + +#pragma mark API + +- (void)dealloc { + if (_note != NULL) { + git_note_free(_note); + } +} + +- (git_note *)git_note { + return _note; +} + +- (NSString *)note { + NSString *message = @(git_note_message(self.git_note)); + NSAssert(message, @"message is nil"); + return message; +} + +- (GTSignature *)author { + return [[GTSignature alloc] initWithGitSignature:git_note_author(self.git_note)]; +} + +- (GTSignature *)committer { + return [[GTSignature alloc] initWithGitSignature:git_note_committer(self.git_note)]; +} + +- (GTOID *)targetOID { + return [GTOID oidWithGitOid:git_note_id(self.git_note)]; +} + +- (instancetype)initWithTargetOID:(GTOID *)oid repository:(GTRepository *)repository referenceName:(NSString *)referenceName error:(NSError **)error { + return [self initWithTargetGitOID:(git_oid *)oid.git_oid repository:repository.git_repository referenceName:referenceName.UTF8String error:error]; +} + +- (instancetype)initWithTargetGitOID:(git_oid *)oid repository:(git_repository *)repository referenceName:(const char *)referenceName error:(NSError **)error { + self = [super init]; + if (self == nil) return nil; + + int gitErr = git_note_read(&_note, repository, referenceName, oid); + + if (gitErr != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitErr description:@"Unable to read note"]; + return nil; + } + + return self; +} + +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + ++ (NSString *)defaultReferenceNameForRepository:(GTRepository *)repository error:(NSError **)error { + NSString *noteRef = nil; + + git_buf default_ref_name = { 0 }; + int gitErr = git_note_default_ref(&default_ref_name, repository.git_repository); + if (gitErr != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitErr description:@"Unable to get default git notes reference name"]; + return nil; + } + + if (default_ref_name.ptr != NULL) { + noteRef = @(default_ref_name.ptr); + } else { + if (error != NULL) *error = [NSError git_errorFor:GIT_ERROR description:@"Unable to get default git notes reference name"]; + } + + git_buf_dispose(&default_ref_name); + + return noteRef; +} +@end diff --git a/ObjectiveGit/GTOID.h b/ObjectiveGit/GTOID.h new file mode 100644 index 000000000..ab0590ee7 --- /dev/null +++ b/ObjectiveGit/GTOID.h @@ -0,0 +1,92 @@ +// +// GTOID.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 4/9/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/oid.h" +#import "GTObject.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Represents an object ID. +@interface GTOID : NSObject + +/// The SHA pointed to by the OID. +@property (nonatomic, readonly, copy) NSString *SHA; + +/// Is the OID all zero? This usually indicates that the object has not been +/// inserted into the ODB yet. +@property (nonatomic, readonly, assign, getter = isZero) BOOL zero; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver with the given git_oid. Designated initializer. +/// +/// git_oid - The underlying git_oid. Cannot be NULL. +/// +/// Returns the initialized receiver. +- (instancetype)initWithGitOid:(const git_oid *)git_oid NS_DESIGNATED_INITIALIZER; + +/// Initializes the receiver by converting the given SHA to an OID. +/// +/// SHA - The to convert to an OID. Cannot be nil. +/// +/// Returns the initialized receiver. +- (instancetype _Nullable)initWithSHA:(NSString *)SHA; + +/// Initializes the receiver by converting the given SHA to an OID +/// optionally returning a NSError instance on failure. +/// +/// SHA - The to convert to an OID. Cannot be nil. +/// error - Will be filled with an error object in if the SHA cannot be parsed +/// +/// Returns the initialized receiver or nil if an error occured. +- (instancetype _Nullable)initWithSHA:(NSString *)SHA error:(NSError **)error; + +/// Initializes the receiver by converting the given SHA C string to an OID. +/// +/// string - The C string to convert. Cannot be NULL. +/// +/// Returns the initialized receiver. +- (instancetype _Nullable)initWithSHACString:(const char *)string; + +/// Initializes the receiver by converting the given SHA C string to an OID +/// optionally returning a NSError instance on failure. +/// +/// string - The C string to convert. Cannot be NULL. +/// error - Will be filled with an error object in if the SHA cannot be parsed +/// +/// Returns the initialized receiver. +- (instancetype _Nullable)initWithSHACString:(const char *)string error:(NSError **)error NS_DESIGNATED_INITIALIZER; + +/// Creates a new instance with the given git_oid using initWithGitOid: ++ (instancetype)oidWithGitOid:(const git_oid *)git_oid; + +/// Creates a new instance from the given SHA string using initWithSHAString: ++ (instancetype _Nullable)oidWithSHA:(NSString *)SHA; + +/// Creates a new instance from the given SHA C string using initWithSHACString: ++ (instancetype _Nullable)oidWithSHACString:(const char *)SHA; + +/// Returns the underlying git_oid struct. +- (const git_oid *)git_oid __attribute__((objc_returns_inner_pointer)); + +@end + +@interface GTOID (GTObjectDatabase) + +/// Calculates an OID by hashing the passed data and object type. +/// +/// data - The data to hash. Cannot be nil. +/// type - The type of the git object. +/// +/// Returns a new OID, or nil if an error occurred. ++ (instancetype _Nullable)OIDByHashingData:(NSData *)data type:(GTObjectType)type error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTOID.m b/ObjectiveGit/GTOID.m similarity index 71% rename from Classes/GTOID.m rename to ObjectiveGit/GTOID.m index b4bf971e8..8010c2885 100644 --- a/Classes/GTOID.m +++ b/ObjectiveGit/GTOID.m @@ -9,6 +9,9 @@ #import "GTOID.h" #import "NSError+Git.h" +#import "git2/errors.h" +#import "git2/odb.h" + @interface GTOID () { git_oid _git_oid; } @@ -24,19 +27,22 @@ - (const git_oid *)git_oid { } - (NSString *)SHA { - char *SHA = malloc(GIT_OID_HEXSZ); - if (SHA == NULL) return nil; - - git_oid_fmt(SHA, self.git_oid); - - NSString *str = [[NSString alloc] initWithBytesNoCopy:SHA length:GIT_OID_HEXSZ encoding:NSUTF8StringEncoding freeWhenDone:YES]; - if (str == nil) free(SHA); + char *SHA = git_oid_tostr_s(self.git_oid); + NSString *str = [[NSString alloc] initWithBytes:SHA + length:GIT_OID_HEXSZ + encoding:NSUTF8StringEncoding]; + NSAssert(str != nil, @"Failed to create SHA string"); return str; } #pragma mark Lifecycle -- (id)initWithGitOid:(const git_oid *)oid { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithGitOid:(const git_oid *)oid { NSParameterAssert(oid != NULL); self = [super init]; @@ -47,16 +53,18 @@ - (id)initWithGitOid:(const git_oid *)oid { return self; } -- (id)initWithSHA:(NSString *)SHA error:(NSError **)error { +- (instancetype)initWithSHA:(NSString *)SHA error:(NSError **)error { NSParameterAssert(SHA != nil); - return [self initWithSHACString:SHA.UTF8String error:error]; + const char *SHACString = SHA.UTF8String; + NSAssert(SHACString, @"Unexpected nil SHA"); + return [self initWithSHACString:SHACString error:error]; } -- (id)initWithSHA:(NSString *)SHA { +- (instancetype)initWithSHA:(NSString *)SHA { return [self initWithSHA:SHA error:NULL]; } -- (id)initWithSHACString:(const char *)string error:(NSError **)error { +- (instancetype)initWithSHACString:(const char *)string error:(NSError **)error { NSParameterAssert(string != NULL); self = [super init]; @@ -73,7 +81,7 @@ - (id)initWithSHACString:(const char *)string error:(NSError **)error { return self; } -- (id)initWithSHACString:(const char *)string { +- (instancetype)initWithSHACString:(const char *)string { return [self initWithSHACString:string error:NULL]; } @@ -89,6 +97,10 @@ + (instancetype)oidWithSHACString:(const char *)SHA { return [[self alloc] initWithSHACString:SHA]; } +- (BOOL)isZero { + return git_oid_iszero(self.git_oid) != 0; +} + #pragma mark NSObject - (NSString *)description { @@ -121,7 +133,7 @@ + (instancetype)OIDByHashingData:(NSData *)data type:(GTObjectType)type error:(N NSParameterAssert(data != nil); git_oid oid; - int gitError = git_odb_hash(&oid, data.bytes, data.length, (git_otype)type); + int gitError = git_odb_hash(&oid, data.bytes, data.length, (git_object_t)type); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to hash"]; return nil; diff --git a/ObjectiveGit/GTObject.h b/ObjectiveGit/GTObject.h new file mode 100644 index 000000000..a74ed418c --- /dev/null +++ b/ObjectiveGit/GTObject.h @@ -0,0 +1,89 @@ +// +// GTObject.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 2/22/11. +// +// The MIT License +// +// Copyright (c) 2011 Tim Clem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "git2/types.h" + +typedef NS_ENUM(int, GTObjectType) { + GTObjectTypeAny = GIT_OBJECT_ANY, /**< Object can be any of the following */ + GTObjectTypeBad = GIT_OBJECT_INVALID, /**< Object is invalid. */ + GTObjectTypeCommit = GIT_OBJECT_COMMIT, /**< A commit object. */ + GTObjectTypeTree = GIT_OBJECT_TREE, /**< A tree (directory listing) object. */ + GTObjectTypeBlob = GIT_OBJECT_BLOB, /**< A file revision object. */ + GTObjectTypeTag = GIT_OBJECT_TAG, /**< An annotated tag object. */ + GTObjectTypeOffsetDelta = GIT_OBJECT_OFS_DELTA,/**< A delta, base is given by an offset. */ + GTObjectTypeRefDelta = GIT_OBJECT_REF_DELTA, /**< A delta, base is given by object id. */ +}; + +@class GTRepository; +@class GTOdbObject; +@class GTOID; + +NS_ASSUME_NONNULL_BEGIN + +@interface GTObject : NSObject + +@property (nonatomic, readonly) NSString *type; +@property (nonatomic, readonly) NSString *SHA; +@property (nonatomic, readonly) NSString *shortSHA; +@property (nonatomic, readonly, strong) GTRepository *repository; +@property (nonatomic, readonly) GTOID *OID; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer. +- (id _Nullable)initWithObj:(git_object *)theObject inRepository:(GTRepository *)theRepo NS_DESIGNATED_INITIALIZER; + +/// Class convenience initializer ++ (id _Nullable)objectWithObj:(git_object *)theObject inRepository:(GTRepository *)theRepo; + +/// The underlying `git_object`. +- (git_object *)git_object __attribute__((objc_returns_inner_pointer)); + +/// Read the raw object from the datastore +/// +/// error(out) - will be filled if an error occurs +/// +/// returns a GTOdbObject or nil if an error occurred. +- (GTOdbObject * _Nullable)odbObjectWithError:(NSError **)error; + +/// Recursively peel an object until an object of the specified type is met. +/// +/// type - The type of the requested object. If you pass GTObjectTypeAny +/// the object will be peeled until the type changes (e.g. a tag will +/// be chased until the referenced object is no longer a tag). +/// error - Will be filled with a NSError object on failure. +/// May be NULL. +/// +/// Returns the found object or nil on error. +- (id _Nullable)objectByPeelingToType:(GTObjectType)type error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTObject.m b/ObjectiveGit/GTObject.m similarity index 88% rename from Classes/GTObject.m rename to ObjectiveGit/GTObject.m index e16ac1ff0..503585092 100644 --- a/Classes/GTObject.m +++ b/ObjectiveGit/GTObject.m @@ -38,6 +38,8 @@ #import "GTTag.h" #import "GTOID.h" +#import "git2/errors.h" + @interface GTObject () @property (nonatomic, readonly, assign) git_object *git_object; @end @@ -68,6 +70,11 @@ - (BOOL)isEqual:(id)otherObject { #pragma mark API +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (id)initWithObj:(git_object *)object inRepository:(GTRepository *)repo { NSParameterAssert(object != NULL); NSParameterAssert(repo != nil); @@ -75,18 +82,18 @@ - (id)initWithObj:(git_object *)object inRepository:(GTRepository *)repo { NSAssert(object_repo == repo.git_repository, @"object %p doesn't belong to repo %@", object, repo); Class objectClass = nil; - git_otype t = git_object_type(object); + git_object_t t = git_object_type(object); switch (t) { - case GIT_OBJ_COMMIT: + case GIT_OBJECT_COMMIT: objectClass = [GTCommit class]; break; - case GIT_OBJ_TREE: + case GIT_OBJECT_TREE: objectClass = [GTTree class]; break; - case GIT_OBJ_BLOB: + case GIT_OBJECT_BLOB: objectClass = [GTBlob class]; break; - case GIT_OBJ_TAG: + case GIT_OBJECT_TAG: objectClass = [GTTag class]; break; default: @@ -94,7 +101,7 @@ - (id)initWithObj:(git_object *)object inRepository:(GTRepository *)repo { } if (!objectClass) { - NSLog(@"Unknown git_otype %s (%d)", git_object_type2string(t), (int)t); + NSLog(@"Unknown git_object_t %s (%d)", git_object_type2string(t), (int)t); return nil; } @@ -116,7 +123,9 @@ + (id)objectWithObj:(git_object *)theObject inRepository:(GTRepository *)theRepo } - (NSString *)type { - return [NSString stringWithUTF8String:git_object_type2string(git_object_type(self.git_object))]; + NSString *type = [NSString stringWithUTF8String:git_object_type2string(git_object_type(self.git_object))]; + NSAssert(type != nil, @"type was nil"); + return type; } - (GTOID *)OID { @@ -140,7 +149,7 @@ - (GTOdbObject *)odbObjectWithError:(NSError **)error { - (id)objectByPeelingToType:(GTObjectType)type error:(NSError **)error { git_object *peeled = NULL; - int gitError = git_object_peel(&peeled, self.git_object, (git_otype)type); + int gitError = git_object_peel(&peeled, self.git_object, (git_object_t)type); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Cannot peel object"]; return nil; diff --git a/Classes/GTObjectDatabase.h b/ObjectiveGit/GTObjectDatabase.h similarity index 55% rename from Classes/GTObjectDatabase.h rename to ObjectiveGit/GTObjectDatabase.h index 0e173a175..c111d8b6a 100644 --- a/Classes/GTObjectDatabase.h +++ b/ObjectiveGit/GTObjectDatabase.h @@ -27,42 +27,48 @@ @class GTOID; +NS_ASSUME_NONNULL_BEGIN + @interface GTObjectDatabase : NSObject @property (nonatomic, readonly, strong) GTRepository *repository; -// Initializes the object database with the given repository. -// -// repo - The repository from which the object database should be created. -// Cannot be nil. -// error - The error if one occurred. -// -// Returns the initialized object. -- (id)initWithRepository:(GTRepository *)repo error:(NSError **)error; +- (instancetype)init NS_UNAVAILABLE; -// The underlying `git_odb` object. +/// Initializes the object database with the given repository. Designated initializer. +/// +/// repo - The repository from which the object database should be created. +/// Cannot be nil. +/// error - The error if one occurred. +/// +/// Returns the initialized object. +- (instancetype _Nullable)initWithRepository:(GTRepository *)repo error:(NSError **)error NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_odb` object. - (git_odb *)git_odb __attribute__((objc_returns_inner_pointer)); -- (GTOdbObject *)objectWithOID:(GTOID *)OID error:(NSError **)error; -- (GTOdbObject *)objectWithSHA:(NSString *)SHA error:(NSError **)error; +- (GTOdbObject * _Nullable)objectWithOID:(GTOID *)OID error:(NSError **)error; +- (GTOdbObject * _Nullable)objectWithSHA:(NSString *)SHA error:(NSError **)error; -// Writes the data into the object database. -// -// data - The data to write. Cannot be nil. -// type - The type of object to create with the data. -// error - The error if one occurred. -// -// Returns the OID for the object which was written, or nil if an error -// occurred. -- (GTOID *)writeData:(NSData *)data type:(GTObjectType)type error:(NSError **)error; +/// Writes the data into the object database. +/// +/// data - The data to write. Cannot be nil. +/// type - The type of object to create with the data. +/// error - The error if one occurred. +/// +/// Returns the OID for the object which was written, or nil if an error +/// occurred. +- (GTOID * _Nullable)writeData:(NSData *)data type:(GTObjectType)type error:(NSError **)error; - (BOOL)containsObjectWithSHA:(NSString *)SHA error:(NSError **)error; -// Checks if the object database contains an object with a given OID. -// -// oid - Object ID to check -// -// Returns YES if the object exists or NO otherwise. +/// Checks if the object database contains an object with a given OID. +/// +/// oid - Object ID to check +/// +/// Returns YES if the object exists or NO otherwise. - (BOOL)containsObjectWithOID:(GTOID *)oid; @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTObjectDatabase.m b/ObjectiveGit/GTObjectDatabase.m similarity index 93% rename from Classes/GTObjectDatabase.m rename to ObjectiveGit/GTObjectDatabase.m index 20d1a576d..99c431a48 100644 --- a/Classes/GTObjectDatabase.m +++ b/ObjectiveGit/GTObjectDatabase.m @@ -32,7 +32,10 @@ #import "GTOID.h" #import "EXTScope.h" +#import "git2/errors.h" +#import "git2/odb.h" #import "git2/odb_backend.h" +#import "git2/repository.h" @interface GTObjectDatabase () @property (nonatomic, readonly, assign) git_odb *git_odb; @@ -53,7 +56,12 @@ - (void)dealloc { #pragma mark API -- (id)initWithRepository:(GTRepository *)repo error:(NSError **)error { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithRepository:(GTRepository *)repo error:(NSError **)error { NSParameterAssert(repo != nil); self = [super init]; @@ -94,7 +102,7 @@ - (GTOID *)writeData:(NSData *)data type:(GTObjectType)type error:(NSError **)er NSParameterAssert(data != nil); git_odb_stream *stream; - int gitError = git_odb_open_wstream(&stream, self.git_odb, data.length, (git_otype)type); + int gitError = git_odb_open_wstream(&stream, self.git_odb, data.length, (git_object_t)type); @onExit { if (stream != NULL) git_odb_stream_free(stream); }; diff --git a/ObjectiveGit/GTOdbObject.h b/ObjectiveGit/GTOdbObject.h new file mode 100644 index 000000000..389bce721 --- /dev/null +++ b/ObjectiveGit/GTOdbObject.h @@ -0,0 +1,41 @@ +// +// GTOdbObject.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 3/23/11. +// Copyright 2011 GitHub, Inc. All rights reserved. +// + +#import "GTObject.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GTOdbObject : NSObject + +/// The repository in which the object resides. +@property (nonatomic, readonly, strong) GTRepository *repository; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the object with the underlying libgit2 object and repository. Designated initializer. +/// +/// object - The underlying libgit2 object. Cannot be NULL. +/// repository - The repository in which the object resides. Cannot be nil. +/// +/// Returns the initialized object. +- (instancetype _Nullable)initWithOdbObj:(git_odb_object *)object repository:(GTRepository *)repository NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_odb_object`. +- (git_odb_object *)git_odb_object __attribute__((objc_returns_inner_pointer)); + +- (NSString * _Nullable)shaHash; +- (GTObjectType)type; +- (size_t)length; +- (NSData * _Nullable)data; + +/// The object ID of this object. +@property (nonatomic, readonly) GTOID * _Nullable OID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTOdbObject.m b/ObjectiveGit/GTOdbObject.m similarity index 86% rename from Classes/GTOdbObject.m rename to ObjectiveGit/GTOdbObject.m index 4fcc08ef6..09c13b7ea 100644 --- a/Classes/GTOdbObject.m +++ b/ObjectiveGit/GTOdbObject.m @@ -9,6 +9,7 @@ #import "GTOdbObject.h" #import "NSString+Git.h" #import "GTOID.h" +#import "git2/odb.h" @interface GTOdbObject () @property (nonatomic, assign, readonly) git_odb_object *git_odb_object; @@ -30,7 +31,12 @@ - (NSString *)description { #pragma mark API -- (id)initWithOdbObj:(git_odb_object *)object repository:(GTRepository *)repository { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithOdbObj:(git_odb_object *)object repository:(GTRepository *)repository { NSParameterAssert(object != NULL); NSParameterAssert(repository != nil); diff --git a/ObjectiveGit/GTReference.h b/ObjectiveGit/GTReference.h new file mode 100644 index 000000000..c24a9fc7b --- /dev/null +++ b/ObjectiveGit/GTReference.h @@ -0,0 +1,160 @@ +// +// GTReference.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 3/2/11. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "GTObject.h" +#import "git2/oid.h" + +@class GTOID; +@class GTReflog; + +typedef NS_ENUM(NSInteger, GTReferenceErrorCode) { + GTReferenceErrorCodeInvalidReference = -4, +}; + +typedef NS_OPTIONS(NSInteger, GTReferenceType) { + GTReferenceTypeInvalid = GIT_REFERENCE_INVALID, /** Invalid reference */ + GTReferenceTypeDirect = GIT_REFERENCE_DIRECT, /** A reference which points at an object id */ + GTReferenceTypeSymbolic = GIT_REFERENCE_SYMBOLIC, /** A reference which points at another reference */ +}; + +NS_ASSUME_NONNULL_BEGIN + +@class GTRepository; + +/// A git reference object +/// +/// References are considered to be equivalent iff both their `name` and +/// `unresolvedTarget` are equal. +@interface GTReference : NSObject + +@property (nonatomic, readonly, strong) GTRepository *repository; +@property (nonatomic, readonly) GTReferenceType referenceType; +@property (nonatomic, readonly) const git_oid *git_oid; +@property (nonatomic, strong, readonly) GTOID * _Nullable OID; + +/// Whether this is a tag. +@property (nonatomic, readonly, getter = isTag) BOOL tag; + +/// Whether this is a local branch. +@property (nonatomic, readonly, getter = isBranch) BOOL branch; + +/// Whether this is a remote-tracking branch. +@property (nonatomic, readonly, getter = isRemote) BOOL remote; + +/// Whether this is a note ref. +@property (nonatomic, readonly, getter = isNote) BOOL note; + +/// The reflog for the reference. +@property (nonatomic, readonly, strong) GTReflog * _Nullable reflog; + +/// Convenience initializers ++ (instancetype _Nullable)referenceByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error; +- (instancetype _Nullable)initByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer. +/// +/// ref - The reference to wrap. Must not be nil. +/// repository - The repository containing the reference. Must not be nil. +/// +/// Returns the initialized receiver. +- (instancetype _Nullable)initWithGitReference:(git_reference *)ref repository:(GTRepository *)repository NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_reference` object. +- (git_reference *)git_reference __attribute__((objc_returns_inner_pointer)); + +/// The target (either GTObject or GTReference) to which the reference points. +@property (nonatomic, readonly, copy) id _Nullable unresolvedTarget; + +/// The resolved object to which the reference points. +@property (nonatomic, readonly, copy) id _Nullable resolvedTarget; + +/// The last direct reference in a chain +@property (nonatomic, readonly, copy) GTReference *resolvedReference; + +/// The OID of the target object. +@property (nonatomic, readonly, copy, nullable) GTOID *targetOID; + +/// Updates the on-disk reference to point to the target and returns the updated +/// reference. +/// +/// Note that this does *not* change the receiver's target. +/// +/// newTarget - The target for the new reference. This must not be nil. +/// message - A message to use when creating the reflog entry for this action. +/// This may be nil. +/// error - The error if one occurred. +/// +/// Returns the updated reference, or nil if an error occurred. +- (GTReference * _Nullable)referenceByUpdatingTarget:(NSString *)newTarget message:(NSString * _Nullable)message error:(NSError **)error; + +/// The name of the reference. +@property (nonatomic, readonly, copy) NSString *name; + +/// Updates the on-disk reference to the name and returns the renamed reference. +/// +/// Note that this does *not* change the receiver's name. +/// +/// newName - The new name for the reference. Cannot be nil. +/// error - The error if one occurred. +/// +/// Returns the renamed reference, or nil if an error occurred. +- (GTReference * _Nullable)referenceByRenaming:(NSString *)newName error:(NSError **)error; + +/// Delete this reference. +/// +/// error - The error if one occurred. +/// +/// Returns whether the deletion was successful. +- (BOOL)deleteWithError:(NSError **)error; + +/// Resolve this reference as a symbolic ref +/// +/// error(out) - will be filled if an error occurs +/// +/// returns the peeled GTReference or nil if an error occurred. +- (GTReference * _Nullable)resolvedReferenceWithError:(NSError **)error; + +/// Reload the reference from disk. +/// +/// error - The error if one occurred. +/// +/// Returns the reloaded reference, or nil if an error occurred. +- (GTReference * _Nullable)reloadedReferenceWithError:(NSError **)error; + +/// An error indicating that the git_reference is no longer valid. ++ (NSError *)invalidReferenceError; + +/// Checks if a reference name is acceptable. +/// +/// refName - The name to be checked. +/// +/// Returns YES if the name is valid or NO otherwise. ++ (BOOL)isValidReferenceName:(NSString *)refName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTReference.m b/ObjectiveGit/GTReference.m similarity index 74% rename from Classes/GTReference.m rename to ObjectiveGit/GTReference.m index b1e7b7575..080375dd2 100644 --- a/Classes/GTReference.m +++ b/ObjectiveGit/GTReference.m @@ -24,12 +24,17 @@ // #import "GTReference.h" + #import "GTOID.h" #import "GTReflog+Private.h" -#import "GTRepository.h" #import "GTRepository+Private.h" +#import "GTRepository.h" +#import "GTSignature.h" #import "NSError+Git.h" #import "NSString+Git.h" +#import "GTRepository+References.h" + +#import "git2/errors.h" @interface GTReference () @property (nonatomic, readonly, assign) git_reference *git_reference; @@ -40,7 +45,7 @@ @interface GTReference () case GTReferenceTypeInvalid: return @"invalid"; - case GTReferenceTypeOid: + case GTReferenceTypeDirect: return @"direct"; case GTReferenceTypeSymbolic: @@ -65,29 +70,16 @@ - (BOOL)isRemote { return git_reference_is_remote(self.git_reference) != 0; } -+ (id)referenceByLookingUpReferencedNamed:(NSString *)refName inRepository:(GTRepository *)theRepo error:(NSError **)error { - return [[self alloc] initByLookingUpReferenceNamed:refName inRepository:theRepo error:error]; -} - -+ (id)referenceByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error { ++ (instancetype)referenceByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error { return [[self alloc] initByResolvingSymbolicReference:symbolicRef error:error]; } -- (id)initByLookingUpReferenceNamed:(NSString *)refName inRepository:(GTRepository *)repo error:(NSError **)error { - NSParameterAssert(refName != nil); - NSParameterAssert(repo != nil); - - git_reference *ref = NULL; - int gitError = git_reference_lookup(&ref, repo.git_repository, refName.UTF8String); - if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to lookup reference %@.", refName]; - return nil; - } - - return [self initWithGitReference:ref repository:repo]; +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; } -- (id)initByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error { +- (instancetype)initByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError **)error { NSParameterAssert(symbolicRef != nil); git_reference *ref = NULL; @@ -100,7 +92,7 @@ - (id)initByResolvingSymbolicReference:(GTReference *)symbolicRef error:(NSError return [self initWithGitReference:ref repository:symbolicRef.repository]; } -- (id)initWithGitReference:(git_reference *)ref repository:(GTRepository *)repo { +- (instancetype)initWithGitReference:(git_reference *)ref repository:(GTRepository *)repo { NSParameterAssert(ref != NULL); NSParameterAssert(repo != nil); @@ -113,21 +105,35 @@ - (id)initWithGitReference:(git_reference *)ref repository:(GTRepository *)repo return self; } +- (BOOL)isBranch { + return git_reference_is_branch(self.git_reference) != 0; +} + +- (BOOL)isTag { + return git_reference_is_tag(self.git_reference) != 0; +} + +- (BOOL)isNote { + return git_reference_is_note(self.git_reference) != 0; +} + - (NSString *)name { - const char *refName = git_reference_name(self.git_reference); - if (refName == NULL) return nil; - - return @(refName); + const char *cRefName = git_reference_name(self.git_reference); + NSAssert(cRefName != nil, @"Unexpected nil name"); + + NSString *refName = @(cRefName); + NSAssert(refName, @"refname is nil"); + return refName; } - (GTReference *)referenceByRenaming:(NSString *)newName error:(NSError **)error { NSParameterAssert(newName != nil); - + git_reference *newRef = NULL; - int gitError = git_reference_rename(&newRef, self.git_reference, newName.UTF8String, 0, [self.repository userSignatureForNow].git_signature, NULL); + int gitError = git_reference_rename(&newRef, self.git_reference, newName.UTF8String, 0, NULL); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to rename reference %@ to %@.", self.name, newName]; - return NO; + return nil; } return [[self.class alloc] initWithGitReference:newRef repository:self.repository]; @@ -138,7 +144,7 @@ - (GTReferenceType)referenceType { } - (id)unresolvedTarget { - if (self.referenceType == GTReferenceTypeOid) { + if (self.referenceType == GTReferenceTypeDirect) { const git_oid *oid = git_reference_target(self.git_reference); if (oid == NULL) return nil; @@ -147,14 +153,14 @@ - (id)unresolvedTarget { NSString *refName = @(git_reference_symbolic_target(self.git_reference)); if (refName == NULL) return nil; - return [self.class referenceByLookingUpReferencedNamed:refName inRepository:self.repository error:NULL]; + return [self.repository lookUpReferenceWithName:refName error:NULL]; } return nil; } - (id)resolvedTarget { git_object *obj; - if (git_reference_peel(&obj, self.git_reference, GIT_OBJ_ANY) != GIT_OK) { + if (git_reference_peel(&obj, self.git_reference, GIT_OBJECT_ANY) != GIT_OK) { return nil; } @@ -162,25 +168,26 @@ - (id)resolvedTarget { } - (GTReference *)resolvedReference { - return [self.class referenceByResolvingSymbolicReference:self error:NULL]; + GTReference *resolvedReference = [self.class referenceByResolvingSymbolicReference:self error:NULL]; + return resolvedReference ? resolvedReference : self; } -- (NSString *)targetSHA { - return [self.resolvedTarget SHA]; +- (GTOID *)targetOID { + return [self.resolvedTarget OID]; } -- (GTReference *)referenceByUpdatingTarget:(NSString *)newTarget committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error { +- (GTReference *)referenceByUpdatingTarget:(NSString *)newTarget message:(NSString *)message error:(NSError **)error { NSParameterAssert(newTarget != nil); int gitError; git_reference *newRef = NULL; - if (git_reference_type(self.git_reference) == GIT_REF_OID) { + if (git_reference_type(self.git_reference) == GIT_REFERENCE_DIRECT) { GTOID *oid = [[GTOID alloc] initWithSHA:newTarget error:error]; if (oid == nil) return nil; - - gitError = git_reference_set_target(&newRef, self.git_reference, oid.git_oid, signature.git_signature, message.UTF8String); + + gitError = git_reference_set_target(&newRef, self.git_reference, oid.git_oid, message.UTF8String); } else { - gitError = git_reference_symbolic_set_target(&newRef, self.git_reference, newTarget.UTF8String, signature.git_signature, message.UTF8String); + gitError = git_reference_symbolic_set_target(&newRef, self.git_reference, newTarget.UTF8String, message.UTF8String); } if (gitError != GIT_OK) { @@ -217,7 +224,7 @@ - (GTOID *)OID { } - (GTReference *)reloadedReferenceWithError:(NSError **)error { - return [[self.class alloc] initByLookingUpReferenceNamed:self.name inRepository:self.repository error:error]; + return [self.repository lookUpReferenceWithName:self.name error:error]; } + (NSError *)invalidReferenceError { diff --git a/ObjectiveGit/GTReflog+Private.h b/ObjectiveGit/GTReflog+Private.h new file mode 100644 index 000000000..812cad515 --- /dev/null +++ b/ObjectiveGit/GTReflog+Private.h @@ -0,0 +1,15 @@ +// +// GTReflog+Private.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 4/9/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTReflog.h" + +@interface GTReflog () + +- (instancetype _Nullable)init NS_UNAVAILABLE; + +@end diff --git a/ObjectiveGit/GTReflog.h b/ObjectiveGit/GTReflog.h new file mode 100644 index 000000000..feeafea51 --- /dev/null +++ b/ObjectiveGit/GTReflog.h @@ -0,0 +1,50 @@ +// +// GTReflog.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 4/9/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import + +@class GTSignature; +@class GTReference; +@class GTReflogEntry; + +NS_ASSUME_NONNULL_BEGIN + +/// A reflog for a reference. Reflogs should not be created manually. Use +/// -[GTReference reflog] to get the reflog for a reference. +@interface GTReflog : NSObject + +/// The number of reflog entries. +@property (nonatomic, readonly, assign) NSUInteger entryCount; + +/// Initializes the receiver with a reference. Designated initializer. +/// +/// reference - The reference whose reflog is being represented. Cannot be nil. +/// +/// Returns the initialized object. +- (instancetype _Nullable)initWithReference:(GTReference * _Nonnull)reference NS_DESIGNATED_INITIALIZER; + +/// Writes a new entry to the reflog. +/// +/// committer - The committer for the reflog entry. Cannot be nil. +/// message - The message to associate with the entry. May be nil. +/// error - The error if one occurred. +/// +/// Returns whether the entry was successfully written. +- (BOOL)writeEntryWithCommitter:(GTSignature *)committer message:(NSString *)message error:(NSError **)error; + +/// Get the reflog entry at the given index. +/// +/// index - The reflog entry to get. 0 is the most recent entry. If it is greater +/// than `entryCount`, it will assert. +/// +/// Returns the entry at that index or nil if not found. +- (GTReflogEntry * _Nullable)entryAtIndex:(NSUInteger)index; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTReflog.m b/ObjectiveGit/GTReflog.m similarity index 90% rename from Classes/GTReflog.m rename to ObjectiveGit/GTReflog.m index 155b55e18..be1ecad6b 100644 --- a/Classes/GTReflog.m +++ b/ObjectiveGit/GTReflog.m @@ -14,6 +14,9 @@ #import "NSError+Git.h" #import "GTReflogEntry+Private.h" +#import "git2/errors.h" +#import "git2/reflog.h" + @interface GTReflog () @property (nonatomic, readonly, assign) git_reflog *git_reflog; @@ -30,7 +33,12 @@ - (void)dealloc { if (_git_reflog != NULL) git_reflog_free(_git_reflog); } -- (id)initWithReference:(GTReference *)reference { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithReference:(GTReference *)reference { NSParameterAssert(reference != nil); NSParameterAssert(reference.name != nil); diff --git a/ObjectiveGit/GTReflogEntry+Private.h b/ObjectiveGit/GTReflogEntry+Private.h new file mode 100644 index 000000000..b30415160 --- /dev/null +++ b/ObjectiveGit/GTReflogEntry+Private.h @@ -0,0 +1,26 @@ +// +// GTReflogEntry+Private.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 4/9/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTReflogEntry.h" +#import "git2/types.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GTReflogEntry () + +/// Initializes the receiver with the underlying reflog entry. Designated initializer. +/// +/// entry - The reflog entry. Cannot be NULL. +/// reflog - The reflog in which the entry resides. Cannot be nil. +/// +/// Returns the initialized object. +- (instancetype _Nullable)initWithGitReflogEntry:(const git_reflog_entry *)entry reflog:(GTReflog *)reflog NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/ObjectiveGit/GTReflogEntry.h b/ObjectiveGit/GTReflogEntry.h new file mode 100644 index 000000000..99fcc503f --- /dev/null +++ b/ObjectiveGit/GTReflogEntry.h @@ -0,0 +1,29 @@ +// +// GTReflogEntry.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 4/9/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import + +@class GTOID; +@class GTSignature; + +/// An entry in a GTReflog. +@interface GTReflogEntry : NSObject + +/// The OID of the ref before the entry. +@property (nonatomic, readonly, strong) GTOID * _Nullable previousOID; + +/// The OID of the ref when the entry was made. +@property (nonatomic, readonly, strong) GTOID * _Nullable updatedOID; + +/// The person who committed the entry. +@property (nonatomic, readonly, strong) GTSignature * _Nullable committer; + +/// The message associated with the entry. +@property (nonatomic, readonly, copy) NSString * _Nullable message; + +@end diff --git a/Classes/GTReflogEntry.m b/ObjectiveGit/GTReflogEntry.m similarity index 91% rename from Classes/GTReflogEntry.m rename to ObjectiveGit/GTReflogEntry.m index e84d0658c..7035b2900 100644 --- a/Classes/GTReflogEntry.m +++ b/ObjectiveGit/GTReflogEntry.m @@ -11,6 +11,8 @@ #import "GTOID.h" #import "GTSignature.h" +#import "git2/reflog.h" + @interface GTReflogEntry () @property (nonatomic, readonly, assign) const git_reflog_entry *git_reflog_entry; @@ -25,7 +27,7 @@ @implementation GTReflogEntry #pragma mark Lifecycle -- (id)initWithGitReflogEntry:(const git_reflog_entry *)entry reflog:(GTReflog *)reflog { +- (instancetype)initWithGitReflogEntry:(const git_reflog_entry *)entry reflog:(GTReflog *)reflog { NSParameterAssert(entry != NULL); NSParameterAssert(reflog != nil); diff --git a/ObjectiveGit/GTRemote.h b/ObjectiveGit/GTRemote.h new file mode 100644 index 000000000..bde9a37f1 --- /dev/null +++ b/ObjectiveGit/GTRemote.h @@ -0,0 +1,145 @@ +// +// GTRemote.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 9/12/12. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/remote.h" + +@class GTRepository; +@class GTOID; +@class GTReference; +@class GTCredentialProvider; + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * const GTRemoteRenameProblematicRefSpecs; + +// Auto Tag settings. See `git_remote_autotag_option_t`. +typedef enum { + GTRemoteDownloadTagsAuto = GIT_REMOTE_DOWNLOAD_TAGS_AUTO, + GTRemoteDownloadTagsNone = GIT_REMOTE_DOWNLOAD_TAGS_NONE, + GTRemoteDownloadTagsAll = GIT_REMOTE_DOWNLOAD_TAGS_ALL, +} GTRemoteAutoTagOption; + +/// A class representing a remote for a git repository. +/// +/// Analogous to `git_remote` in libgit2. +@interface GTRemote : NSObject + +/// The repository owning this remote. +@property (nonatomic, readonly, strong) GTRepository *repository; + +/// The name of the remote. +@property (nonatomic, readonly, copy) NSString * _Nullable name; + +/// The URL string for the remote. +@property (nonatomic, readonly, copy) NSString * _Nullable URLString; + +/// The push URL for the remote, if provided. +@property (nonatomic, copy) NSString * _Nullable pushURLString; + +/// Whether the remote is connected or not. +@property (nonatomic, readonly, getter=isConnected) BOOL connected; + +/// Whether the remote updates FETCH_HEAD when fetched. +/// Defaults to YES. +@property (nonatomic) BOOL updatesFetchHead; + +/// The auto-tag setting for the remote. +@property (nonatomic) GTRemoteAutoTagOption autoTag; + +/// The fetch refspecs for this remote. +/// +/// This array will contain NSStrings of the form +/// `+refs/heads/*:refs/remotes/REMOTE/*`. +@property (nonatomic, readonly, copy) NSArray * _Nullable fetchRefspecs; + +/// The push refspecs for this remote. +/// +/// This array will contain NSStrings of the form +/// `+refs/heads/*:refs/remotes/REMOTE/*`. +@property (nonatomic, readonly, copy) NSArray * _Nullable pushRefspecs; + +/// Tests if a name is valid ++ (BOOL)isValidRemoteName:(NSString *)name; + +/// Create a new remote in a repository. +/// +/// name - The name for the new remote. Cannot be nil. +/// URLString - The origin URL for the remote. Cannot be nil. +/// repo - The repository the remote should be created in. Cannot be nil. +/// error - Will be set if an error occurs. +/// +/// Returns a new remote, or nil if an error occurred ++ (instancetype _Nullable)createRemoteWithName:(NSString *)name URLString:(NSString *)URLString inRepository:(GTRepository *)repo error:(NSError **)error; + +/// Load a remote from a repository. +/// +/// name - The name for the new remote. Cannot be nil. +/// repo - The repository the remote should be looked up in. Cannot be nil. +/// error - Will be set if an error occurs. +/// +/// Returns the loaded remote, or nil if an error occurred. ++ (instancetype _Nullable)remoteWithName:(NSString *)name inRepository:(GTRepository *)repo error:(NSError **)error; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initialize a remote from a `git_remote`. Designated initializer. +/// +/// remote - The underlying `git_remote` object. Cannot be nil. +/// repo - The repository the remote belongs to. Cannot be nil. +/// +/// Returns the initialized receiver, or nil if an error occurred. +- (instancetype _Nullable)initWithGitRemote:(git_remote *)remote inRepository:(GTRepository *)repo NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_remote` object. +- (git_remote *)git_remote __attribute__((objc_returns_inner_pointer)); + +/// Rename the remote. +/// +/// name - The new name for the remote. Cannot be nil. +/// error - Will be set if an error occurs. If there was an error renaming some +/// refspecs, their names will be available as an arry under the +/// `GTRemoteRenameProblematicRefSpecs` key. +/// +/// Return YES if successful, NO otherwise. +- (BOOL)rename:(NSString *)name error:(NSError **)error; + +/// Updates the URL string for this remote. +/// +/// URLString - The URLString to update to. May not be nil. +/// error - If not NULL, this will be set to any error that occurs when +/// updating the URLString or saving the remote. +/// +/// Returns YES if the URLString was successfully updated, NO and an error +/// if updating or saving the remote failed. +- (BOOL)updateURLString:(NSString *)URLString error:(NSError **)error; + +/// Updates the push URL string for this remote. +/// +/// URLString - The URLString to update to. May not be nil. +/// error - If not NULL, this will be set to any error that occurs when +/// updating the URLString or saving the remote. +/// +/// Returns YES if the push URLString was successfully updated, NO and an error +/// if updating or saving the remote failed. +- (BOOL)updatePushURLString:(NSString *)URLString error:(NSError **)error; + +/// Adds a fetch refspec to this remote. +/// +/// fetchRefspec - The fetch refspec string to add. May not be nil. +/// error - If not NULL, this will be set to any error that occurs +/// when adding the refspec or saving the remote. +/// +/// Returns YES if there is the refspec is successfully added +/// or a matching refspec is already present, NO and an error if +/// adding the refspec or saving the remote failed. +- (BOOL)addFetchRefspec:(NSString *)fetchRefspec error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRemote.m b/ObjectiveGit/GTRemote.m new file mode 100644 index 000000000..03f76b7ca --- /dev/null +++ b/ObjectiveGit/GTRemote.m @@ -0,0 +1,237 @@ +// +// GTRemote.m +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 9/12/12. +// Copyright (c) 2012 GitHub, Inc. All rights reserved. +// + +#import "GTRemote.h" +#import "GTRepository.h" +#import "GTOID.h" +#import "GTCredential+Private.h" +#import "GTBranch.h" + +#import "NSError+Git.h" +#import "NSArray+StringArray.h" +#import "EXTScope.h" + +#import "git2/errors.h" + +NSString * const GTRemoteRenameProblematicRefSpecs = @"GTRemoteRenameProblematicRefSpecs"; + +@interface GTRemote () + +@property (nonatomic, readonly, assign) git_remote *git_remote; +@end + +@implementation GTRemote + +#pragma mark Lifecycle + ++ (instancetype)createRemoteWithName:(NSString *)name URLString:(NSString *)URLString inRepository:(GTRepository *)repo error:(NSError **)error { + NSParameterAssert(name != nil); + NSParameterAssert(URLString != nil); + NSParameterAssert(repo != nil); + + git_remote *remote; + int gitError = git_remote_create(&remote, repo.git_repository, name.UTF8String, URLString.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Remote creation failed" failureReason:@"Failed to create a remote named \"%@\" for \"%@\"", name, URLString]; + + return nil; + } + + return [[self alloc] initWithGitRemote:remote inRepository:repo]; +} + ++ (instancetype)remoteWithName:(NSString *)name inRepository:(GTRepository *)repo error:(NSError **)error { + NSParameterAssert(name != nil); + NSParameterAssert(repo != nil); + + git_remote *remote; + int gitError = git_remote_lookup(&remote, repo.git_repository, name.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Remote loading failed" failureReason:nil]; + + return nil; + } + + return [[self alloc] initWithGitRemote:remote inRepository:repo]; +} + +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithGitRemote:(git_remote *)remote inRepository:(GTRepository *)repo { + NSParameterAssert(remote != NULL); + NSParameterAssert(repo != nil); + + self = [super init]; + if (self == nil) return nil; + + _git_remote = remote; + _repository = repo; + + return self; +} + +- (void)dealloc { + if (_git_remote != NULL) git_remote_free(_git_remote); +} + +#pragma mark NSObject + +- (BOOL)isEqual:(GTRemote *)object { + if (object == self) return YES; + if (![object isKindOfClass:[self class]]) return NO; + + return [object.name isEqual:self.name] && [object.URLString isEqual:self.URLString]; +} + +- (NSUInteger)hash { + return self.name.hash ^ self.URLString.hash; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> name: %@, URLString: %@", NSStringFromClass([self class]), self, self.name, self.URLString]; +} + +#pragma mark API + ++ (BOOL)isValidRemoteName:(NSString *)name { + NSParameterAssert(name != nil); + + return (git_remote_is_valid_name(name.UTF8String) == 1 ? YES : NO); +} + +#pragma mark Properties + +- (NSString *)name { + const char *name = git_remote_name(self.git_remote); + if (name == NULL) return nil; + + return @(name); +} + +- (NSString *)URLString { + const char *URLString = git_remote_url(self.git_remote); + if (URLString == NULL) return nil; + + return @(URLString); +} + +- (void)setURLString:(NSString *)URLString { + git_remote_set_url(self.repository.git_repository, self.name.UTF8String, URLString.UTF8String); +} + +- (NSString *)pushURLString { + const char *pushURLString = git_remote_pushurl(self.git_remote); + if (pushURLString == NULL) return nil; + + return @(pushURLString); +} + +- (void)setPushURLString:(NSString *)pushURLString { + git_remote_set_pushurl(self.repository.git_repository, self.name.UTF8String, pushURLString.UTF8String); +} + +- (BOOL)isConnected { + return git_remote_connected(self.git_remote) != 0; +} + +#pragma mark Renaming + +- (BOOL)rename:(NSString *)name error:(NSError **)error { + NSParameterAssert(name != nil); + + git_strarray problematic_refspecs; + + int gitError = git_remote_rename(&problematic_refspecs, self.repository.git_repository, git_remote_name(self.git_remote), name.UTF8String); + if (gitError != GIT_OK) { + NSArray *problematicRefspecs = [NSArray git_arrayWithStrarray:problematic_refspecs]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:problematicRefspecs forKey:GTRemoteRenameProblematicRefSpecs]; + + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to rename remote" userInfo:userInfo failureReason:@"Couldn't rename remote %@ to %@", self.name, name]; + } + + git_strarray_free(&problematic_refspecs); + + return gitError == GIT_OK; +} + +- (NSArray *)fetchRefspecs { + __block git_strarray refspecs; + int gitError = git_remote_get_fetch_refspecs(&refspecs, self.git_remote); + if (gitError != GIT_OK) return nil; + + @onExit { + git_strarray_free(&refspecs); + }; + + return [NSArray git_arrayWithStrarray:refspecs]; + +} + +- (NSArray *)pushRefspecs { + __block git_strarray refspecs; + int gitError = git_remote_get_push_refspecs(&refspecs, self.git_remote); + if (gitError != GIT_OK) return nil; + + @onExit { + git_strarray_free(&refspecs); + }; + + return [NSArray git_arrayWithStrarray:refspecs]; +} + +#pragma mark Update the remote + +- (BOOL)updateURLString:(NSString *)URLString error:(NSError **)error { + NSParameterAssert(URLString != nil); + + if ([self.URLString isEqualToString:URLString]) return YES; + + int gitError = git_remote_set_url(self.repository.git_repository, self.name.UTF8String, URLString.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) { + *error = [NSError git_errorFor:gitError description:@"Failed to update remote URL string."]; + } + return NO; + } + return YES; +} + +- (BOOL)updatePushURLString:(NSString *)URLString error:(NSError **)error { + NSParameterAssert(URLString != nil); + + if ([self.pushURLString isEqualToString:URLString]) return YES; + + int gitError = git_remote_set_pushurl(self.repository.git_repository, self.name.UTF8String, URLString.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) { + *error = [NSError git_errorFor:gitError description:@"Failed to update remote push URL string."]; + } + return NO; + } + return YES; +} + +- (BOOL)addFetchRefspec:(NSString *)fetchRefspec error:(NSError **)error { + NSParameterAssert(fetchRefspec != nil); + + if ([self.fetchRefspecs containsObject:fetchRefspec]) return YES; + + int gitError = git_remote_add_fetch(self.repository.git_repository, self.name.UTF8String, fetchRefspec.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) { + *error = [NSError git_errorFor:gitError description:@"Failed to add fetch refspec."]; + } + return NO; + } + return YES; +} + +@end diff --git a/ObjectiveGit/GTRepository+Attributes.h b/ObjectiveGit/GTRepository+Attributes.h new file mode 100644 index 000000000..a86ab6e28 --- /dev/null +++ b/ObjectiveGit/GTRepository+Attributes.h @@ -0,0 +1,25 @@ +// +// GTRepository+Attributes.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 7/25/14. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GTRepository (Attributes) + +/// Look up the value for the attribute of the given name for the given path. +/// +/// name - The name of the attribute to look up. Cannot be nil. +/// path - The path to use for the lookup. Cannot be nil. +/// +/// Returns the value of the attribute or nil. +- (NSString * _Nullable)attributeWithName:(NSString *)name path:(NSString *)path; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRepository+Attributes.m b/ObjectiveGit/GTRepository+Attributes.m new file mode 100644 index 000000000..54e7c029f --- /dev/null +++ b/ObjectiveGit/GTRepository+Attributes.m @@ -0,0 +1,27 @@ +// +// GTRepository+Attributes.m +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 7/25/14. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +#import "GTRepository+Attributes.h" +#import "NSError+Git.h" + +#import "git2/attr.h" + +@implementation GTRepository (Attributes) + +- (NSString *)attributeWithName:(NSString *)name path:(NSString *)path { + NSParameterAssert(name != nil); + NSParameterAssert(path != nil); + + const char *val = NULL; + git_attr_get(&val, self.git_repository, GIT_ATTR_CHECK_FILE_THEN_INDEX, path.UTF8String, name.UTF8String); + if (val == NULL) return nil; + + return @(val); +} + +@end diff --git a/ObjectiveGit/GTRepository+Blame.h b/ObjectiveGit/GTRepository+Blame.h new file mode 100644 index 000000000..603274f25 --- /dev/null +++ b/ObjectiveGit/GTRepository+Blame.h @@ -0,0 +1,51 @@ +// +// GTRepository+Blame.h +// ObjectiveGitFramework +// +// Created by Ezekiel Pierson on 2/5/14. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +#import +#import "GTRepository.h" + +#import "git2/blame.h" + +@class GTBlame; + +/// Enum for options passed to the dictionary in `-blameWithFile:inRepository:options:` +/// +/// For flag documentation see `blame.h`. +typedef NS_OPTIONS(NSInteger, GTBlameOptions) { + GTBlameOptionsNormal = GIT_BLAME_NORMAL, +}; + +/// A `NSNumber` wrapped `GTBlameOptions`. Flags are documented above. +extern NSString * const GTBlameOptionsFlags; + +/// A `GTOID` determining the newest commit to consider. +/// Default is HEAD. +extern NSString * const GTBlameOptionsNewestCommitOID; + +/// A `GTOID` determining the oldest commit to consider. +/// Default is the first commit without a parent. +extern NSString * const GTBlameOptionsOldestCommitOID; + +/// The first line in the file to blame. Default is 1. +extern NSString * const GTBlameOptionsFirstLine; + +/// The last line in the file to blame. Default is the last line. +extern NSString * const GTBlameOptionsLastLine; + +@interface GTRepository (Blame) + +/// Create a blame for a file, with options. +/// +/// path - Path for the file to examine. Can't be nil +/// options - A dictionary consiting of the above keys. May be nil. +/// error - Populated with an `NSError` object on error. +/// +/// Returns a new `GTBlame` object or nil if an error occurred. +- (GTBlame *)blameWithFile:(NSString *)path options:(NSDictionary *)options error:(NSError **)error; + +@end diff --git a/Classes/GTRepository+Blame.m b/ObjectiveGit/GTRepository+Blame.m similarity index 96% rename from Classes/GTRepository+Blame.m rename to ObjectiveGit/GTRepository+Blame.m index 2e9eecfe2..1138eae3b 100644 --- a/Classes/GTRepository+Blame.m +++ b/ObjectiveGit/GTRepository+Blame.m @@ -7,7 +7,10 @@ // #import "GTRepository+Blame.h" -#import "git2.h" + +#import "git2/errors.h" +#import "GTBlame.h" +#import "NSError+Git.h" NSString * const GTBlameOptionsFlags = @"GTBlameOptionsFlags"; NSString * const GTBlameOptionsNewestCommitOID = @"GTBlameOptionsNewestCommitOID"; diff --git a/ObjectiveGit/GTRepository+Committing.h b/ObjectiveGit/GTRepository+Committing.h new file mode 100644 index 000000000..4e5518664 --- /dev/null +++ b/ObjectiveGit/GTRepository+Committing.h @@ -0,0 +1,36 @@ +// +// GTRepository+Committing.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 9/30/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GTRepository (Committing) + +/// Creates a new commit. +/// +/// tree - The tree used for the commit. Cannot be nil. +/// message - The commit message. Cannot be nil. +/// author - The author of the commit. Cannot be nil. +/// committer - The committer of the commit. Cannot be nil. +/// parents - An array of GTCommits. May be nil, which means the commit has no +/// parents. +/// refName - The ref name which will be updated to point at the new commit. +/// May be nil. +/// error - The error if one occurred. +/// +/// Returns the newly created commit, or nil if an error occurred. +- (GTCommit * _Nullable)createCommitWithTree:(GTTree *)tree message:(NSString *)message author:(GTSignature *)author committer:(GTSignature *)committer parents:(NSArray * _Nullable)parents updatingReferenceNamed:(NSString * _Nullable)refName error:(NSError **)error; + +/// Creates a new commit using +createCommitWithTree:message:author:committer:parents:updatingReferenceNamed:error: +/// with -userSignatureForNow as both the author and committer. +- (GTCommit * _Nullable)createCommitWithTree:(GTTree *)tree message:(NSString *)message parents:(NSArray * _Nullable)parents updatingReferenceNamed:(NSString * _Nullable)refName error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTRepository+Committing.m b/ObjectiveGit/GTRepository+Committing.m similarity index 93% rename from Classes/GTRepository+Committing.m rename to ObjectiveGit/GTRepository+Committing.m index ff2664c08..93e4e7480 100644 --- a/Classes/GTRepository+Committing.m +++ b/ObjectiveGit/GTRepository+Committing.m @@ -9,6 +9,13 @@ #import "GTRepository+Committing.h" #import "GTRepository+Private.h" +#import "git2/errors.h" +#import "git2/commit.h" +#import "GTCommit.h" +#import "GTSignature.h" +#import "GTTree.h" +#import "NSError+Git.h" + @implementation GTRepository (Committing) - (GTCommit *)createCommitWithTree:(GTTree *)tree message:(NSString *)message parents:(NSArray *)parents updatingReferenceNamed:(NSString *)refName error:(NSError **)error { diff --git a/ObjectiveGit/GTRepository+Merging.h b/ObjectiveGit/GTRepository+Merging.h new file mode 100644 index 000000000..53649a095 --- /dev/null +++ b/ObjectiveGit/GTRepository+Merging.h @@ -0,0 +1,80 @@ +// +// GTRepository+Merging.h +// ObjectiveGitFramework +// +// Created by Piet Brauer on 02/03/16. +// Copyright © 2016 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" +#import "GTIndexEntry.h" +#import "git2/merge.h" + +NS_ASSUME_NONNULL_BEGIN + +/// UserInfo key for conflicted files when pulling fails with a merge conflict +extern NSString * const GTPullMergeConflictedFiles; + +/// An enum describing the result of the merge analysis. +/// See `git_merge_analysis_t`. +typedef NS_OPTIONS(NSInteger, GTMergeAnalysis) { + GTMergeAnalysisNone = GIT_MERGE_ANALYSIS_NONE, + GTMergeAnalysisNormal = GIT_MERGE_ANALYSIS_NORMAL, + GTMergeAnalysisUpToDate = GIT_MERGE_ANALYSIS_UP_TO_DATE, + GTMergeAnalysisUnborn = GIT_MERGE_ANALYSIS_UNBORN, + GTMergeAnalysisFastForward = GIT_MERGE_ANALYSIS_FASTFORWARD, +}; + +@interface GTRepository (Merging) + +/// Enumerate all available merge head entries. +/// +/// error - The error if one ocurred. Can be NULL. +/// block - A block to execute for each MERGE_HEAD entry. `mergeHeadEntry` will +/// be the current merge head entry. Setting `stop` to YES will cause +/// enumeration to stop after the block returns. Must not be nil. +/// +/// Returns YES if the operation succedded, NO otherwise. +- (BOOL)enumerateMergeHeadEntriesWithError:(NSError **)error usingBlock:(void (^)(GTOID *mergeHeadEntry, BOOL *stop))block; + +/// Convenience method for -enumerateMergeHeadEntriesWithError:usingBlock: that retuns an NSArray with all the fetch head entries. +/// +/// error - The error if one ocurred. Can be NULL. +/// +/// Retruns a (possibly empty) array with GTOID objects. Will not be nil. +- (NSArray *)mergeHeadEntriesWithError:(NSError **)error; + +/// Merge Branch into current branch +/// +/// fromBranch - The branch to merge from. +/// error - The error if one occurred. Can be NULL. +/// +/// Returns YES if the merge was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)fromBranch withError:(NSError **)error; + +/// Gets the file content with conflict markers for the given file +/// +/// The parameters taked are the ones received from `enumerateConflictedFiles`. +/// +/// ancestor - The ancestor entry +/// ours - The index entry of our side +/// theirs - The index entry of their side +/// error - The error if one occurred. Can be NULL. +/// +/// Returns The file content annotated with conflict markers or null on error +- (NSString * _Nullable)contentsOfDiffWithAncestor:(GTIndexEntry *)ancestor ourSide:(GTIndexEntry *)ourSide theirSide:(GTIndexEntry *)theirSide error:(NSError **)error; + +/// Analyze which merge to perform. +/// +/// analysis - The resulting analysis. +/// fromBranch - The branch to merge from. +/// error - The error if one occurred. Can be NULL. +/// +/// Returns YES if the analysis was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)analyzeMerge:(GTMergeAnalysis *)analysis fromBranch:(GTBranch *)fromBranch error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRepository+Merging.m b/ObjectiveGit/GTRepository+Merging.m new file mode 100644 index 000000000..f7b9837df --- /dev/null +++ b/ObjectiveGit/GTRepository+Merging.m @@ -0,0 +1,288 @@ +// +// GTRepository+Merging.m +// ObjectiveGitFramework +// +// Created by Piet Brauer on 02/03/16. +// Copyright © 2016 GitHub, Inc. All rights reserved. +// + +#import "GTRepository+Merging.h" +#import "GTOID.h" +#import "NSError+Git.h" +#import "git2/errors.h" +#import "GTCommit.h" +#import "GTReference.h" +#import "GTRepository+Committing.h" +#import "GTRepository+Pull.h" +#import "GTTree.h" +#import "GTIndex.h" +#import "GTIndexEntry.h" +#import "GTOdbObject.h" +#import "GTObjectDatabase.h" + +typedef void (^GTRemoteFetchTransferProgressBlock)(const git_transfer_progress *stats, BOOL *stop); + +@implementation GTRepository (Merging) + +typedef void (^GTRepositoryEnumerateMergeHeadEntryBlock)(GTOID *entry, BOOL *stop); + +typedef struct { + __unsafe_unretained GTRepositoryEnumerateMergeHeadEntryBlock enumerationBlock; +} GTEnumerateMergeHeadEntriesPayload; + +int GTMergeHeadEntriesCallback(const git_oid *oid, void *payload) { + GTEnumerateMergeHeadEntriesPayload *entriesPayload = payload; + + GTRepositoryEnumerateMergeHeadEntryBlock enumerationBlock = entriesPayload->enumerationBlock; + + GTOID *gtoid = [GTOID oidWithGitOid:oid]; + + BOOL stop = NO; + + enumerationBlock(gtoid, &stop); + + return (stop == YES ? GIT_EUSER : 0); +} + +- (BOOL)enumerateMergeHeadEntriesWithError:(NSError **)error usingBlock:(void (^)(GTOID *mergeHeadEntry, BOOL *stop))block { + NSParameterAssert(block != nil); + + GTEnumerateMergeHeadEntriesPayload payload = { + .enumerationBlock = block, + }; + + int gitError = git_repository_mergehead_foreach(self.git_repository, GTMergeHeadEntriesCallback, &payload); + + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get mergehead entries"]; + return NO; + } + + return YES; +} + +- (NSArray *)mergeHeadEntriesWithError:(NSError **)error { + NSMutableArray *entries = [NSMutableArray array]; + + [self enumerateMergeHeadEntriesWithError:error usingBlock:^(GTOID *mergeHeadEntry, BOOL *stop) { + [entries addObject:mergeHeadEntry]; + + *stop = NO; + }]; + + return entries; +} + +- (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)branch withError:(NSError **)error { + // Check if merge is necessary + GTBranch *localBranch = [self currentBranchWithError:error]; + if (!localBranch) { + return NO; + } + + GTCommit *localCommit = [localBranch targetCommitWithError:error]; + if (!localCommit) { + return NO; + } + + GTCommit *remoteCommit = [branch targetCommitWithError:error]; + if (!remoteCommit) { + return NO; + } + + if ([localCommit.SHA isEqualToString:remoteCommit.SHA]) { + // Local and remote tracking branch are already in sync + return YES; + } + + GTMergeAnalysis analysis = GTMergeAnalysisNone; + BOOL success = [self analyzeMerge:&analysis fromBranch:branch error:error]; + if (!success) { + return NO; + } + + if (analysis & GTMergeAnalysisUpToDate) { + // Nothing to do + return YES; + } else if (analysis & GTMergeAnalysisFastForward || + analysis & GTMergeAnalysisUnborn) { + // Fast-forward branch + NSString *message = [NSString stringWithFormat:@"merge %@: Fast-forward", branch.name]; + GTReference *reference = [localBranch.reference referenceByUpdatingTarget:remoteCommit.SHA message:message error:error]; + BOOL checkoutSuccess = [self checkoutReference:reference options:[GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategyForce] error:error]; + return checkoutSuccess; + } else if (analysis & GTMergeAnalysisNormal) { + // Do normal merge + GTTree *localTree = localCommit.tree; + GTTree *remoteTree = remoteCommit.tree; + + // TODO: Find common ancestor + GTTree *ancestorTree = nil; + + // Merge + GTIndex *index = [localTree merge:remoteTree ancestor:ancestorTree error:error]; + if (!index) { + return NO; + } + + // Check for conflict + if (index.hasConflicts) { + NSMutableArray *files = [NSMutableArray array]; + [index enumerateConflictedFilesWithError:error usingBlock:^(GTIndexEntry * _Nonnull ancestor, GTIndexEntry * _Nonnull ours, GTIndexEntry * _Nonnull theirs, BOOL * _Nonnull stop) { + [files addObject:ours.path]; + }]; + + if (error != NULL) { + NSDictionary *userInfo = @{GTPullMergeConflictedFiles: files}; + *error = [NSError git_errorFor:GIT_ECONFLICT description:@"Merge conflict" userInfo:userInfo failureReason:nil]; + } + + // Write conflicts + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + checkout_opts.checkout_strategy = (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS); + + git_annotated_commit *annotatedCommit; + [self annotatedCommit:&annotatedCommit fromCommit:remoteCommit error:error]; + + git_merge(self.git_repository, (const git_annotated_commit **)&annotatedCommit, 1, &merge_opts, &checkout_opts); + + return NO; + } + + GTTree *newTree = [index writeTreeToRepository:self error:error]; + if (!newTree) { + return NO; + } + + // Create merge commit + NSString *message = [NSString stringWithFormat:@"Merge branch '%@'", localBranch.shortName]; + NSArray *parents = @[ localCommit, remoteCommit ]; + + // FIXME: This is stepping on the local tree + GTCommit *mergeCommit = [self createCommitWithTree:newTree message:message parents:parents updatingReferenceNamed:localBranch.reference.name error:error]; + if (!mergeCommit) { + return NO; + } + + BOOL success = [self checkoutReference:localBranch.reference options:[GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategyForce] error:error]; + return success; + } + + return NO; +} + +- (NSString * _Nullable)contentsOfDiffWithAncestor:(GTIndexEntry *)ancestor ourSide:(GTIndexEntry *)ourSide theirSide:(GTIndexEntry *)theirSide error:(NSError **)error { + + GTObjectDatabase *database = [self objectDatabaseWithError:error]; + if (database == nil) { + return nil; + } + + // initialize the ancestor's merge file input + git_merge_file_input ancestorInput; + int gitError = git_merge_file_init_input(&ancestorInput, GIT_MERGE_FILE_INPUT_VERSION); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input for ancestor"]; + return nil; + } + + git_oid ancestorId = ancestor.git_index_entry->id; + GTOID *ancestorOID = [[GTOID alloc] initWithGitOid:&ancestorId]; + NSData *ancestorData = [[database objectWithOID:ancestorOID error: error] data]; + if (ancestorData == nil) { + return nil; + } + ancestorInput.ptr = ancestorData.bytes; + ancestorInput.size = ancestorData.length; + + + // initialize our merge file input + git_merge_file_input ourInput; + gitError = git_merge_file_init_input(&ourInput, GIT_MERGE_FILE_INPUT_VERSION); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input for our side"]; + return nil; + } + + git_oid ourId = ourSide.git_index_entry->id; + GTOID *ourOID = [[GTOID alloc] initWithGitOid:&ourId]; + NSData *ourData = [[database objectWithOID:ourOID error: error] data]; + if (ourData == nil) { + return nil; + } + ourInput.ptr = ourData.bytes; + ourInput.size = ourData.length; + + + // initialize their merge file input + git_merge_file_input theirInput; + gitError = git_merge_file_init_input(&theirInput, GIT_MERGE_FILE_INPUT_VERSION); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file input other side"]; + return nil; + } + + git_oid theirId = theirSide.git_index_entry->id; + GTOID *theirOID = [[GTOID alloc] initWithGitOid:&theirId]; + NSData *theirData = [[database objectWithOID:theirOID error: error] data]; + if (theirData == nil) { + return nil; + } + theirInput.ptr = theirData.bytes; + theirInput.size = theirData.length; + + + git_merge_file_result result; + gitError = git_merge_file(&result, &ancestorInput, &ourInput, &theirInput, nil); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create merge file"]; + return nil; + } + + NSString *mergedContent = [[NSString alloc] initWithBytes:result.ptr length:result.len encoding:NSUTF8StringEncoding]; + + git_merge_file_result_free(&result); + + return mergedContent; +} + +- (BOOL)annotatedCommit:(git_annotated_commit **)annotatedCommit fromCommit:(GTCommit *)fromCommit error:(NSError **)error { + int gitError = git_annotated_commit_lookup(annotatedCommit, self.git_repository, fromCommit.OID.git_oid); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to lookup annotated commit for %@", fromCommit]; + return NO; + } + + return YES; +} + +- (BOOL)analyzeMerge:(GTMergeAnalysis *)analysis fromBranch:(GTBranch *)fromBranch error:(NSError **)error { + NSParameterAssert(analysis != NULL); + NSParameterAssert(fromBranch != nil); + + GTCommit *fromCommit = [fromBranch targetCommitWithError:error]; + if (!fromCommit) { + return NO; + } + + git_annotated_commit *annotatedCommit; + [self annotatedCommit:&annotatedCommit fromCommit:fromCommit error:error]; + + // Allow fast-forward or normal merge + git_merge_preference_t preference = GIT_MERGE_PREFERENCE_NONE; + + // Merge analysis + int gitError = git_merge_analysis((git_merge_analysis_t *)analysis, &preference, self.git_repository, (const git_annotated_commit **) &annotatedCommit, 1); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to analyze merge"]; + return NO; + } + + // Cleanup + git_annotated_commit_free(annotatedCommit); + + return YES; +} + +@end diff --git a/ObjectiveGit/GTRepository+Private.h b/ObjectiveGit/GTRepository+Private.h new file mode 100644 index 000000000..f3341730c --- /dev/null +++ b/ObjectiveGit/GTRepository+Private.h @@ -0,0 +1,20 @@ +// +// GTRepository+Private.h +// ObjectiveGitFramework +// +// Created by Etienne on 15/07/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface GTRepository () + +- (id _Nullable)lookUpObjectByGitOid:(const git_oid *)oid objectType:(GTObjectType)type error:(NSError **)error; +- (id _Nullable)lookUpObjectByGitOid:(const git_oid *)oid error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRepository+Pull.h b/ObjectiveGit/GTRepository+Pull.h new file mode 100644 index 000000000..cbc1f74fd --- /dev/null +++ b/ObjectiveGit/GTRepository+Pull.h @@ -0,0 +1,37 @@ +// +// GTRepository+Pull.h +// ObjectiveGitFramework +// +// Created by Ben Chatelain on 6/17/15. +// Copyright © 2015 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^GTRemoteFetchTransferProgressBlock)(const git_transfer_progress *progress, BOOL *stop); + +@interface GTRepository (Pull) + +#pragma mark - Pull + +/// Pull a single branch from a remote. +/// +/// branch - The branch to pull. +/// remote - The remote to pull from. +/// options - Options applied to the fetch operation. +/// Recognized options are: +/// `GTRepositoryRemoteOptionsCredentialProvider` +/// `GTRepositoryRemoteOptionsFetchPrune` +/// `GTRepositoryRemoteOptionsDownloadTags` +/// error - The error if one occurred. Can be NULL. +/// progressBlock - An optional callback for monitoring progress. +/// +/// Returns YES if the pull was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)pullBranch:(GTBranch *)branch fromRemote:(GTRemote *)remote withOptions:(NSDictionary * _Nullable)options error:(NSError **)error progress:(GTRemoteFetchTransferProgressBlock _Nullable)progressBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRepository+Pull.m b/ObjectiveGit/GTRepository+Pull.m new file mode 100644 index 000000000..b99bf37ce --- /dev/null +++ b/ObjectiveGit/GTRepository+Pull.m @@ -0,0 +1,55 @@ +// +// GTRepository+Pull.m +// ObjectiveGitFramework +// +// Created by Ben Chatelain on 6/17/15. +// Copyright © 2015 GitHub, Inc. All rights reserved. +// + +#import "GTRepository+Pull.h" + +#import "GTCommit.h" +#import "GTRepository+RemoteOperations.h" +#import "NSError+Git.h" +#import "git2/errors.h" +#import "GTRepository+Merging.h" + +NSString * const GTPullMergeConflictedFiles = @"GTPullMergeConflictedFiles"; + +@implementation GTRepository (Pull) + +#pragma mark - Pull + +- (BOOL)pullBranch:(GTBranch *)branch fromRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemoteFetchTransferProgressBlock)progressBlock { + NSParameterAssert(branch != nil); + NSParameterAssert(remote != nil); + + GTRepository *repo = branch.repository; + + if (![self fetchRemote:remote withOptions:options error:error progress:progressBlock]) { + return NO; + } + + // Get tracking branch after fetch so that it is up-to-date and doesn't need to be refreshed from disk + GTBranch *trackingBranch; + if (branch.branchType == GTBranchTypeLocal) { + BOOL success = NO; + trackingBranch = [branch trackingBranchWithError:error success:&success]; + if (!success) { + if (error != NULL) *error = [NSError git_errorFor:GIT_ERROR description:@"Tracking branch not found for %@", branch.name]; + return NO; + } + else if (!trackingBranch) { + // Error should already be provided by libgit2 + return NO; + } + } + else { + // When given a remote branch, use it as the tracking branch + trackingBranch = branch; + } + + return [repo mergeBranchIntoCurrentBranch:trackingBranch withError:error]; +} + +@end diff --git a/ObjectiveGit/GTRepository+References.h b/ObjectiveGit/GTRepository+References.h new file mode 100644 index 000000000..cfddac2fc --- /dev/null +++ b/ObjectiveGit/GTRepository+References.h @@ -0,0 +1,27 @@ +// +// GTRepository+References.h +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 6/4/15. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" + +NS_ASSUME_NONNULL_BEGIN + +@class GTReference; + +@interface GTRepository (References) + +/// Look up a reference by name. +/// +/// name - The name of the reference to look up. Cannot be nil. +/// error - The error if one occurs. May be NULL. +/// +/// Returns the reference or nil if look up failed. +- (GTReference * _Nullable)lookUpReferenceWithName:(NSString *)name error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRepository+References.m b/ObjectiveGit/GTRepository+References.m new file mode 100644 index 000000000..1494d7539 --- /dev/null +++ b/ObjectiveGit/GTRepository+References.m @@ -0,0 +1,30 @@ +// +// GTRepository+References.m +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 6/4/15. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +#import "GTRepository+References.h" +#import "GTReference.h" +#import "NSError+Git.h" + +#import "git2/errors.h" + +@implementation GTRepository (References) + +- (GTReference *)lookUpReferenceWithName:(NSString *)name error:(NSError **)error { + NSParameterAssert(name != nil); + + git_reference *ref = NULL; + int gitError = git_reference_lookup(&ref, self.git_repository, name.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to lookup reference %@.", name]; + return nil; + } + + return [[GTReference alloc] initWithGitReference:ref repository:self]; +} + +@end diff --git a/ObjectiveGit/GTRepository+RemoteOperations.h b/ObjectiveGit/GTRepository+RemoteOperations.h new file mode 100644 index 000000000..d93ebaad7 --- /dev/null +++ b/ObjectiveGit/GTRepository+RemoteOperations.h @@ -0,0 +1,133 @@ +// +// GTRepository+RemoteOperations.h +// ObjectiveGitFramework +// +// Created by Etienne on 18/11/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" +#import "git2/remote.h" + +@class GTFetchHeadEntry; + +NS_ASSUME_NONNULL_BEGIN + +/// A `GTCredentialProvider`, that will be used to authenticate against the remote. +extern NSString *const GTRepositoryRemoteOptionsCredentialProvider; + +/// A `GTFetchPruneOption`, that will be used to determine if the fetch should prune or not. +extern NSString *const GTRepositoryRemoteOptionsFetchPrune; + +/// A `GTRemoteAutoTagOption`, that will be used to determine how the fetch should handle tags. +extern NSString *const GTRepositoryRemoteOptionsDownloadTags; + +/// A `@(BOOL)`, indicating git notes should also be pushed to the default notes reference name (if `@(YES)`), or an `NSArray(NSString)` containing reference names to be pushed through. +extern NSString *const GTRepositoryRemoteOptionsPushNotes; + +/// An enum describing the data needed for pruning. +/// See `git_fetch_prune_t`. +typedef NS_ENUM(NSInteger, GTFetchPruneOption) { + GTFetchPruneOptionUnspecified = GIT_FETCH_PRUNE_UNSPECIFIED, + GTFetchPruneOptionYes = GIT_FETCH_PRUNE, + GTFetchPruneOptionNo = GIT_FETCH_NO_PRUNE, +}; + +@interface GTRepository (RemoteOperations) + +#pragma mark - Fetch + +/// Fetch a remote. +/// +/// remote - The remote to fetch from. Must not be nil. +/// options - Options applied to the fetch operation. May be nil. +/// Recognized options are : +/// `GTRepositoryRemoteOptionsCredentialProvider` +/// `GTRepositoryRemoteOptionsFetchPrune` +/// `GTRepositoryRemoteOptionsDownloadTags` +/// error - The error if one occurred. Can be NULL. +/// progressBlock - Optional callback to receive fetch progress stats during the +/// transfer. May be nil. +/// +/// Returns YES if the fetch was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)fetchRemote:(GTRemote *)remote withOptions:(NSDictionary * _Nullable)options error:(NSError **)error progress:(void (^ _Nullable)(const git_transfer_progress *stats, BOOL *stop))progressBlock; + +/// Enumerate all available fetch head entries. +/// +/// error - The error if one ocurred. Can be NULL. +/// block - A block to execute for each FETCH_HEAD entry. `fetchHeadEntry` will +/// be the current fetch head entry. Setting `stop` to YES will cause +/// enumeration to stop after the block returns. Must not be nil. +/// +/// Returns YES if the operation succedded, NO otherwise. +- (BOOL)enumerateFetchHeadEntriesWithError:(NSError **)error usingBlock:(void (^)(GTFetchHeadEntry *fetchHeadEntry, BOOL *stop))block; + +/// Convenience method for -enumerateFetchHeadEntriesWithError:usingBlock: that retuns an NSArray with all the fetch head entries. +/// +/// error - The error if one ocurred. Can be NULL. +/// +/// Retruns a (possibly empty) array with GTFetchHeadEntry objects. Will not be nil. +- (NSArray *)fetchHeadEntriesWithError:(NSError **)error; + +#pragma mark - Push + +/// Push a single branch to a remote. +/// +/// branch - The branch to push. Must not be nil. +/// remote - The remote to push to. Must not be nil. +/// options - Options applied to the push operation. Can be NULL. +/// Recognized options are: +/// `GTRepositoryRemoteOptionsCredentialProvider` +/// `GTRepositoryRemoteOptionsPushNotes` (to push together with notes in one push) +/// error - The error if one occurred. Can be NULL. +/// progressBlock - An optional callback for monitoring progress. May be NULL. +/// +/// Returns YES if the push was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)pushBranch:(GTBranch *)branch toRemote:(GTRemote *)remote withOptions:(NSDictionary * _Nullable)options error:(NSError **)error progress:(void (^ _Nullable)(unsigned int current, unsigned int total, size_t bytes, BOOL *stop))progressBlock; + +/// Push an array of branches to a remote. +/// +/// branches - An array of branches to push. Must not be nil. +/// remote - The remote to push to. Must not be nil. +/// options - Options applied to the push operation. Can be NULL. +/// Recognized options are: +/// `GTRepositoryRemoteOptionsCredentialProvider`, +/// `GTRepositoryRemoteOptionsPushNotes` (to push together with notes in one push) +/// error - The error if one occurred. Can be NULL. +/// progressBlock - An optional callback for monitoring progress. May be NULL. +/// +/// Returns YES if the push was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)pushBranches:(NSArray *)branches toRemote:(GTRemote *)remote withOptions:(NSDictionary * _Nullable)options error:(NSError **)error progress:(void (^ _Nullable)(unsigned int current, unsigned int total, size_t bytes, BOOL *stop))progressBlock; + +/// Push a given Git notes reference name to a remote. +/// +/// noteReferenceName - Name of the notes reference. If NULL, will default to whatever the default is (e.g. "refs/notes/commits") +/// remote - The remote to push to. Must not be nil. +/// options - Options applied to the push operation. Can be NULL. +/// Recognized options are: +/// `GTRepositoryRemoteOptionsCredentialProvider` +/// error - The error if one occurred. Can be NULL. +/// progressBlock - An optional callback for monitoring progress. May be NULL. +/// +/// Returns YES if the push was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)pushNotes:(NSString * _Nullable)noteReferenceName toRemote:(GTRemote *)remote withOptions:(NSDictionary * _Nullable)options error:(NSError **)error progress:(void (^ _Nullable)(unsigned int current, unsigned int total, size_t bytes, BOOL *stop))progressBlock; + +/// Delete a remote branch +/// +/// branch - The branch to push. Must not be nil. +/// remote - The remote to push to. Must not be nil. +/// options - Options applied to the push operation. Can be NULL. +/// Recognized options are: +/// `GTRepositoryRemoteOptionsCredentialProvider` +/// error - The error if one occurred. Can be NULL. +/// +/// Returns YES if the push was successful, NO otherwise (and `error`, if provided, +/// will point to an error describing what happened). +- (BOOL)deleteBranch:(GTBranch *)branch fromRemote:(GTRemote *)remote withOptions:(NSDictionary * _Nullable)options error:(NSError **)error; +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRepository+RemoteOperations.m b/ObjectiveGit/GTRepository+RemoteOperations.m new file mode 100644 index 000000000..822171ab9 --- /dev/null +++ b/ObjectiveGit/GTRepository+RemoteOperations.m @@ -0,0 +1,320 @@ +// +// GTRepository+RemoteOperations.m +// ObjectiveGitFramework +// +// Created by Etienne on 18/11/13. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTRepository+RemoteOperations.h" + +#import "EXTScope.h" +#import "GTCredential.h" +#import "GTCredential+Private.h" +#import "GTFetchHeadEntry.h" +#import "GTOID.h" +#import "GTRemote.h" +#import "GTSignature.h" +#import "NSArray+StringArray.h" +#import "NSError+Git.h" +#import "GTRepository+References.h" +#import "GTNote.h" + +#import "git2/errors.h" +#import "git2/remote.h" +#import "git2/notes.h" +#import "git2/buffer.h" + +NSString *const GTRepositoryRemoteOptionsCredentialProvider = @"GTRepositoryRemoteOptionsCredentialProvider"; +NSString *const GTRepositoryRemoteOptionsFetchPrune = @"GTRepositoryRemoteOptionsFetchPrune"; +NSString *const GTRepositoryRemoteOptionsDownloadTags = @"GTRepositoryRemoteOptionsDownloadTags"; +NSString *const GTRepositoryRemoteOptionsPushNotes = @"GTRepositoryRemoteOptionsPushNotes"; + +typedef void (^GTRemoteFetchTransferProgressBlock)(const git_transfer_progress *stats, BOOL *stop); +typedef void (^GTRemotePushTransferProgressBlock)(unsigned int current, unsigned int total, size_t bytes, BOOL *stop); + +@implementation GTRepository (RemoteOperations) + +#pragma mark - +#pragma mark Common Remote code + +typedef struct { + GTCredentialAcquireCallbackInfo credProvider; + __unsafe_unretained GTRemoteFetchTransferProgressBlock fetchProgressBlock; + __unsafe_unretained GTRemotePushTransferProgressBlock pushProgressBlock; + git_direction direction; +} GTRemoteConnectionInfo; + +int GTRemoteFetchTransferProgressCallback(const git_transfer_progress *stats, void *payload) { + GTRemoteConnectionInfo *info = payload; + BOOL stop = NO; + + if (info->fetchProgressBlock != nil) { + info->fetchProgressBlock(stats, &stop); + } + + return (stop == YES ? GIT_EUSER : 0); +} + +int GTRemotePushTransferProgressCallback(unsigned int current, unsigned int total, size_t bytes, void *payload) { + GTRemoteConnectionInfo *pushPayload = payload; + + BOOL stop = NO; + if (pushPayload->pushProgressBlock) { + pushPayload->pushProgressBlock(current, total, bytes, &stop); + } + + return (stop == YES ? GIT_EUSER : 0); +} + +#pragma mark - Fetch + +- (BOOL)fetchRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemoteFetchTransferProgressBlock)progressBlock { + + GTCredentialProvider *credProvider = options[GTRepositoryRemoteOptionsCredentialProvider]; + GTRemoteConnectionInfo connectionInfo = { + .credProvider = {credProvider}, + .direction = GIT_DIRECTION_FETCH, + .fetchProgressBlock = progressBlock, + }; + git_remote_callbacks remote_callbacks = { + .version = GIT_REMOTE_CALLBACKS_VERSION, + .credentials = (credProvider != nil ? GTCredentialAcquireCallback : NULL), + .transfer_progress = GTRemoteFetchTransferProgressCallback, + .payload = &connectionInfo, + }; + + git_fetch_options fetchOptions = GIT_FETCH_OPTIONS_INIT; + fetchOptions.callbacks = remote_callbacks; + fetchOptions.prune = [options[GTRepositoryRemoteOptionsFetchPrune] unsignedIntValue]; + fetchOptions.download_tags = [options[GTRepositoryRemoteOptionsDownloadTags] unsignedIntValue]; + + __block git_strarray refspecs; + int gitError = git_remote_get_fetch_refspecs(&refspecs, remote.git_remote); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get fetch refspecs for remote"]; + return NO; + } + + @onExit { + git_strarray_free(&refspecs); + }; + + NSString *reflog_message = [NSString stringWithFormat:@"fetching remote %@", remote.name]; + + gitError = git_remote_fetch(remote.git_remote, &refspecs, &fetchOptions, reflog_message.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to fetch from remote"]; + return NO; + } + + return YES; +} + +#pragma mark - Fetch Head Enumeration + +typedef void (^GTRemoteEnumerateFetchHeadEntryBlock)(GTFetchHeadEntry *entry, BOOL *stop); + +typedef struct { + __unsafe_unretained GTRepository *repository; + __unsafe_unretained GTRemoteEnumerateFetchHeadEntryBlock enumerationBlock; +} GTEnumerateHeadEntriesPayload; + +int GTFetchHeadEntriesCallback(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload) { + GTEnumerateHeadEntriesPayload *entriesPayload = payload; + + GTRepository *repository = entriesPayload->repository; + GTRemoteEnumerateFetchHeadEntryBlock enumerationBlock = entriesPayload->enumerationBlock; + + NSString *refName = @(ref_name); + NSCAssert(refName, @"refName is nil"); + + NSString *remoteURL = @(remote_url); + NSCAssert(remote_url, @"remoteURL is nil"); + + GTReference *reference = [repository lookUpReferenceWithName:refName error:NULL]; + + GTFetchHeadEntry *entry = [[GTFetchHeadEntry alloc] initWithReference:reference remoteURLString:remoteURL targetOID:[GTOID oidWithGitOid:oid] isMerge:(BOOL)is_merge]; + + BOOL stop = NO; + + enumerationBlock(entry, &stop); + + return (stop == YES ? GIT_EUSER : 0); +} + +- (BOOL)enumerateFetchHeadEntriesWithError:(NSError **)error usingBlock:(void (^)(GTFetchHeadEntry *fetchHeadEntry, BOOL *stop))block { + NSParameterAssert(block != nil); + + GTEnumerateHeadEntriesPayload payload = { + .repository = self, + .enumerationBlock = block, + }; + int gitError = git_repository_fetchhead_foreach(self.git_repository, GTFetchHeadEntriesCallback, &payload); + + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get fetchhead entries"]; + return NO; + } + + return YES; +} + +- (NSArray *)fetchHeadEntriesWithError:(NSError **)error { + NSMutableArray *entries = [NSMutableArray array]; + + [self enumerateFetchHeadEntriesWithError:error usingBlock:^(GTFetchHeadEntry *fetchHeadEntry, BOOL *stop) { + [entries addObject:fetchHeadEntry]; + + *stop = NO; + }]; + + return entries; +} + +#pragma mark - Push (Public) + +- (BOOL)pushBranch:(GTBranch *)branch toRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemotePushTransferProgressBlock)progressBlock { + NSParameterAssert(branch != nil); + NSParameterAssert(remote != nil); + + return [self pushBranches:@[ branch ] toRemote:remote withOptions:options error:error progress:progressBlock]; +} + +- (BOOL)pushBranches:(NSArray *)branches toRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemotePushTransferProgressBlock)progressBlock { + NSParameterAssert(branches != nil); + NSParameterAssert(branches.count != 0); + NSParameterAssert(remote != nil); + + NSMutableArray *refspecs = nil; + // Build refspecs for the passed in branches + refspecs = [NSMutableArray arrayWithCapacity:branches.count]; + for (GTBranch *branch in branches) { + // Default remote reference for when branch doesn't exist on remote - create with same short name + NSString *remoteBranchReference = [NSString stringWithFormat:@"refs/heads/%@", branch.shortName]; + + BOOL success = NO; + GTBranch *trackingBranch = [branch trackingBranchWithError:error success:&success]; + + if (success && trackingBranch != nil) { + // Use remote branch short name from trackingBranch, which could be different + // (e.g. refs/heads/master:refs/heads/my_master) + remoteBranchReference = [NSString stringWithFormat:@"refs/heads/%@", trackingBranch.shortName]; + } + + [refspecs addObject:[NSString stringWithFormat:@"refs/heads/%@:%@", branch.shortName, remoteBranchReference]]; + } + + // Also push the notes reference(s), if needed. + id pushNotesOption = options[GTRepositoryRemoteOptionsPushNotes]; + if (pushNotesOption != nil) { + if ([pushNotesOption isKindOfClass:[NSNumber class]]) { // Push notes is a bool, only push the default reference name if it's YES + if ([(NSNumber *)pushNotesOption boolValue]) { + NSString *notesReferenceName = [GTNote defaultReferenceNameForRepository:self error:nil]; + + // Check whether the reference name exists for the repo, or our push will fail + if (notesReferenceName != nil && [self lookUpReferenceWithName:notesReferenceName error:nil] != nil) { + [refspecs addObject:[NSString stringWithFormat:@"%@:%@", notesReferenceName, notesReferenceName]]; + } + } + } else if ([pushNotesOption isKindOfClass:[NSArray class]]) { + for (NSString *notesReferenceName in (NSArray *)pushNotesOption) { + if ([notesReferenceName isKindOfClass:[NSString class]]) { // Just a sanity check, we only accept NSStrings in the array + // Check whether the reference name exists for the repo, or our push will fail + if (notesReferenceName != nil && [self lookUpReferenceWithName:notesReferenceName error:nil] != nil) { + [refspecs addObject:[NSString stringWithFormat:@"%@:%@", notesReferenceName, notesReferenceName]]; + } + } + } + } + } + + return [self pushRefspecs:refspecs toRemote:remote withOptions:options error:error progress:progressBlock]; +} + +- (BOOL)pushNotes:(NSString *)noteRef toRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemotePushTransferProgressBlock)progressBlock { + NSParameterAssert(remote != nil); + + if (noteRef == nil) { + noteRef = [GTNote defaultReferenceNameForRepository:self error:error]; + + if (noteRef == nil) return NO; + } + + GTReference *notesReference = [self lookUpReferenceWithName:noteRef error:error]; + + if (notesReference == nil) return NO; + + return [self pushRefspecs:@[[NSString stringWithFormat:@"%@:%@", noteRef, noteRef]] toRemote:remote withOptions:options error:error progress:progressBlock]; +} + +#pragma mark - Deletion (Public) +- (BOOL)deleteBranch:(GTBranch *)branch fromRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error { + NSParameterAssert(branch != nil); + NSParameterAssert(remote != nil); + + NSArray *refspecs = @[ [NSString stringWithFormat:@":refs/heads/%@", branch.shortName] ]; + + return [self pushRefspecs:refspecs toRemote:remote withOptions:options error:error progress:nil]; +} + +#pragma mark - Push (Private) + +- (BOOL)pushRefspecs:(NSArray *)refspecs toRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemotePushTransferProgressBlock)progressBlock { + + int gitError; + GTCredentialProvider *credProvider = options[GTRepositoryRemoteOptionsCredentialProvider]; + + GTRemoteConnectionInfo connectionInfo = { + .credProvider = { .credProvider = credProvider }, + .direction = GIT_DIRECTION_PUSH, + .pushProgressBlock = progressBlock, + }; + + git_remote_callbacks remote_callbacks = GIT_REMOTE_CALLBACKS_INIT; + remote_callbacks.credentials = (credProvider != nil ? GTCredentialAcquireCallback : NULL); + remote_callbacks.push_transfer_progress = GTRemotePushTransferProgressCallback; + remote_callbacks.payload = &connectionInfo; + + gitError = git_remote_connect(remote.git_remote, GIT_DIRECTION_PUSH, &remote_callbacks, NULL, NULL); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to connect remote"]; + return NO; + } + @onExit { + git_remote_disconnect(remote.git_remote); + }; + + git_push_options push_options = GIT_PUSH_OPTIONS_INIT; + + gitError = git_push_init_options(&push_options, GIT_PUSH_OPTIONS_VERSION); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to init push options"]; + return NO; + } + + push_options.callbacks = remote_callbacks; + + const git_strarray git_refspecs = refspecs.git_strarray; + + gitError = git_remote_upload(remote.git_remote, &git_refspecs, &push_options); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Push upload to remote failed"]; + return NO; + } + + int update_fetchhead = 1; + // Ignored for push + git_remote_autotag_option_t download_tags = GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED; + NSString *reflog_message = [NSString stringWithFormat:@"pushing remote %@", remote.name]; + + gitError = git_remote_update_tips(remote.git_remote, &remote_callbacks, update_fetchhead, download_tags, reflog_message.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Update tips failed"]; + return NO; + } + + return YES; +} + +@end diff --git a/Classes/GTRepository+Reset.h b/ObjectiveGit/GTRepository+Reset.h similarity index 82% rename from Classes/GTRepository+Reset.h rename to ObjectiveGit/GTRepository+Reset.h index e1b31242a..cbb06158a 100644 --- a/Classes/GTRepository+Reset.h +++ b/ObjectiveGit/GTRepository+Reset.h @@ -8,12 +8,16 @@ #import "GTRepository.h" +#import "git2/reset.h" + /// The reset types. See the libgit2 documentation for more info. -typedef enum { +typedef NS_ENUM(NSInteger, GTRepositoryResetType) { GTRepositoryResetTypeSoft = GIT_RESET_SOFT, GTRepositoryResetTypeMixed = GIT_RESET_MIXED, GTRepositoryResetTypeHard = GIT_RESET_HARD, -} GTRepositoryResetType; +}; + +NS_ASSUME_NONNULL_BEGIN @interface GTRepository (Reset) @@ -33,6 +37,8 @@ typedef enum { /// error - The error if one occurred. /// /// Returns whether the reset was successful. -- (BOOL)resetPathspecs:(NSArray *)pathspecs toCommit:(GTCommit *)commit error:(NSError **)error; +- (BOOL)resetPathspecs:(NSArray *)pathspecs toCommit:(GTCommit *)commit error:(NSError **)error; @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTRepository+Reset.m b/ObjectiveGit/GTRepository+Reset.m similarity index 91% rename from Classes/GTRepository+Reset.m rename to ObjectiveGit/GTRepository+Reset.m index 7481a325a..71d24b1f0 100644 --- a/Classes/GTRepository+Reset.m +++ b/ObjectiveGit/GTRepository+Reset.m @@ -12,12 +12,15 @@ #import "NSError+Git.h" #import "GTSignature.h" +#import "git2/errors.h" + @implementation GTRepository (Reset) - (BOOL)resetToCommit:(GTCommit *)commit resetType:(GTRepositoryResetType)resetType error:(NSError **)error { NSParameterAssert(commit != nil); - int gitError = git_reset(self.git_repository, commit.git_object, (git_reset_t)resetType, (git_signature *)[self userSignatureForNow].git_signature, NULL); + git_checkout_options options = GIT_CHECKOUT_OPTIONS_INIT; + int gitError = git_reset(self.git_repository, commit.git_object, (git_reset_t)resetType, &options); if (gitError != GIT_OK) { if (error != NULL) { *error = [NSError git_errorFor:gitError description:@"Failed to reset repository to commit %@.", commit.SHA]; diff --git a/ObjectiveGit/GTRepository+Stashing.h b/ObjectiveGit/GTRepository+Stashing.h new file mode 100644 index 000000000..a7dc57b34 --- /dev/null +++ b/ObjectiveGit/GTRepository+Stashing.h @@ -0,0 +1,97 @@ +// +// GTRepository+Stashing.h +// ObjectiveGitFramework +// +// Created by Justin Spahr-Summers on 2013-09-27. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" + +#import "git2/stash.h" + +/// Flags for -stashChangesWithMessage:flags:error:. +/// Those can be ORed together. See git_stash_flags for additional information. +typedef NS_OPTIONS(NSInteger, GTRepositoryStashFlag) { + GTRepositoryStashFlagDefault = GIT_STASH_DEFAULT, + GTRepositoryStashFlagKeepIndex = GIT_STASH_KEEP_INDEX, + GTRepositoryStashFlagIncludeUntracked = GIT_STASH_INCLUDE_UNTRACKED, + GTRepositoryStashFlagIncludeIgnored = GIT_STASH_INCLUDE_IGNORED +}; + +/// Flags for -applyStashAtIndex:flags:error: and +/// -popStashAtIndex:flags:error. +/// Those can be ORed together. See git_stash_apply_flags for additional information. +typedef NS_OPTIONS(NSInteger, GTRepositoryStashApplyFlag) { + GTRepositoryStashApplyFlagDefault = GIT_STASH_APPLY_DEFAULT, + GTRepositoryStashApplyFlagReinstateIndex = GIT_STASH_APPLY_REINSTATE_INDEX, +}; + +/// Enum representing the current state of a stash apply/pop operation. +/// See git_stash_apply_progress_t for additional information. +typedef NS_ENUM(NSInteger, GTRepositoryStashApplyProgress) { + GTRepositoryStashApplyProgressNone = GIT_STASH_APPLY_PROGRESS_NONE, + GTRepositoryStashApplyProgressLoadingStash = GIT_STASH_APPLY_PROGRESS_LOADING_STASH, + GTRepositoryStashApplyProgressAnalyzeIndex = GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX, + GTRepositoryStashApplyProgressAnalyzeModified = GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED, + GTRepositoryStashApplyProgressAnalyzeUntracked = GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED, + GTRepositoryStashApplyProgressCheckoutUntracked = GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED, + GTRepositoryStashApplyProgressCheckoutModified = GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED, + GTRepositoryStashApplyProgressDone = GIT_STASH_APPLY_PROGRESS_DONE, +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface GTRepository (Stashing) + +/// Stash the repository's changes. +/// +/// message - Message to be attributed to the item in the stash. This may be +/// nil. +/// stashFlag - The flags of stash to be used. +/// error - If not NULL, set to any error that occurred. +/// +/// Returns a commit representing the stashed changes if successful, or nil +/// otherwise. +- (GTCommit * _Nullable)stashChangesWithMessage:(NSString * _Nullable)message flags:(GTRepositoryStashFlag)flags error:(NSError **)error; + +/// Enumerate over all the stashes in the repository, from most recent to oldest. +/// +/// block - A block to execute for each stash found. `index` will be the zero-based +/// stash index (where 0 is the most recent stash). Setting `stop` to YES +/// will cause enumeration to stop after the block returns. Must not be nil. +- (void)enumerateStashesUsingBlock:(void (^)(NSUInteger index, NSString * _Nullable message, GTOID * _Nullable oid, BOOL *stop))block; + +/// Apply stashed changes. +/// +/// index - The index of the stash to apply. 0 is the latest one. +/// flags - The flags to use when applying the stash. +/// options - The options to use when checking out (if nil, use the defaults provided by libgit2). +/// error - If not NULL, set to any error that occurred. +/// progressBlock - A block that will be executed on each step of the stash application. +/// +/// Returns YES if the requested stash was successfully applied, NO otherwise. +- (BOOL)applyStashAtIndex:(NSUInteger)index flags:(GTRepositoryStashApplyFlag)flags checkoutOptions:(GTCheckoutOptions * _Nullable)options error:(NSError **)error progressBlock:(void (^ _Nullable)(GTRepositoryStashApplyProgress progress, BOOL *stop))progressBlock; + +/// Pop stashed changes. +/// +/// index - The index of the stash to apply. 0 is the most recent stash. +/// flags - The flags to use when applying the stash. +/// options - The options to use when checking out (if nil, use the defaults provided by libgit2). +/// error - If not NULL, set to any error that occurred. +/// progressBlock - A block that will be executed on each step of the stash application. +/// +/// Returns YES if the requested stash was successfully applied, NO otherwise. +- (BOOL)popStashAtIndex:(NSUInteger)index flags:(GTRepositoryStashApplyFlag)flags checkoutOptions:(GTCheckoutOptions * _Nullable)options error:(NSError **)error progressBlock:(void (^ _Nullable)(GTRepositoryStashApplyProgress progress, BOOL *stop))progressBlock; + +/// Drop a stash from the repository's list of stashes. +/// +/// index - The index of the stash to drop, where 0 is the most recent stash. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns YES if the stash was successfully dropped, NO otherwise +- (BOOL)dropStashAtIndex:(NSUInteger)index error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ObjectiveGit/GTRepository+Stashing.m b/ObjectiveGit/GTRepository+Stashing.m new file mode 100644 index 000000000..e5903a87f --- /dev/null +++ b/ObjectiveGit/GTRepository+Stashing.m @@ -0,0 +1,116 @@ +// +// GTRepository+Stashing.m +// ObjectiveGitFramework +// +// Created by Justin Spahr-Summers on 2013-09-27. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTRepository+Stashing.h" +#import "GTOID.h" +#import "GTRepository+Private.h" +#import "GTSignature.h" +#import "NSError+Git.h" + +#import "git2/errors.h" + +typedef void (^GTRepositoryStashEnumerationBlock)(NSUInteger index, NSString *message, GTOID *oid, BOOL *stop); + +@implementation GTRepository (Stashing) + +- (GTCommit *)stashChangesWithMessage:(NSString *)message flags:(GTRepositoryStashFlag)flags error:(NSError **)error { + git_oid git_oid; + + int gitError = git_stash_save(&git_oid, self.git_repository, [self userSignatureForNow].git_signature, message.UTF8String, flags); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to stash."]; + return nil; + } + + return [self lookUpObjectByGitOid:&git_oid error:error]; +} + +static int stashEnumerationCallback(size_t index, const char *message, const git_oid *stash_id, void *payload) { + GTRepositoryStashEnumerationBlock block = (__bridge GTRepositoryStashEnumerationBlock)payload; + + NSString *messageString = nil; + if (message != NULL) messageString = @(message); + + GTOID *stashOID = [[GTOID alloc] initWithGitOid:stash_id]; + + BOOL stop = NO; + block(index, messageString, stashOID, &stop); + + return (stop ? GIT_EUSER : 0); +} + +- (void)enumerateStashesUsingBlock:(GTRepositoryStashEnumerationBlock)block { + NSParameterAssert(block != nil); + + git_stash_foreach(self.git_repository, &stashEnumerationCallback, (__bridge void *)block); +} + +static int stashApplyProgressCallback(git_stash_apply_progress_t progress, void *payload) { + void (^block)(GTRepositoryStashApplyProgress, BOOL *) = (__bridge id)payload; + + BOOL stop = NO; + block((GTRepositoryStashApplyProgress)progress, &stop); + + return (stop ? GIT_EUSER : 0); +} + +- (BOOL)applyStashAtIndex:(NSUInteger)index flags:(GTRepositoryStashApplyFlag)flags checkoutOptions:(GTCheckoutOptions *)options error:(NSError **)error progressBlock:(void (^)(GTRepositoryStashApplyProgress progress, BOOL *stop))progressBlock { + git_stash_apply_options stash_options = GIT_STASH_APPLY_OPTIONS_INIT; + + stash_options.flags = (git_stash_apply_flags)flags; + + if (progressBlock != nil) { + stash_options.progress_cb = stashApplyProgressCallback; + stash_options.progress_payload = (__bridge void *)progressBlock; + } + + if (options != nil) { + stash_options.checkout_options = *options.git_checkoutOptions; + } + + int gitError = git_stash_apply(self.git_repository, index, &stash_options); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Stash apply failed" failureReason:@"The stash at index %ld couldn't be applied.", (unsigned long)index]; + return NO; + } + return YES; +} + +- (BOOL)popStashAtIndex:(NSUInteger)index flags:(GTRepositoryStashApplyFlag)flags checkoutOptions:(GTCheckoutOptions *)options error:(NSError **)error progressBlock:(void (^)(GTRepositoryStashApplyProgress progress, BOOL *stop))progressBlock { + git_stash_apply_options stash_options = GIT_STASH_APPLY_OPTIONS_INIT; + + stash_options.flags = (git_stash_apply_flags)flags; + + if (progressBlock != nil) { + stash_options.progress_cb = stashApplyProgressCallback; + stash_options.progress_payload = (__bridge void *)progressBlock; + } + + if (options != nil) { + stash_options.checkout_options = *options.git_checkoutOptions; + } + + int gitError = git_stash_pop(self.git_repository, index, &stash_options); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Stash pop failed" failureReason:@"The stash at index %ld couldn't be applied.", (unsigned long)index]; + return NO; + } + return YES; +} + +- (BOOL)dropStashAtIndex:(NSUInteger)index error:(NSError **)error { + int gitError = git_stash_drop(self.git_repository, index); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Stash drop failed" failureReason:@"The stash at index %ld couldn't be dropped", (unsigned long)index]; + return NO; + } + + return YES; +} + +@end diff --git a/ObjectiveGit/GTRepository+Status.h b/ObjectiveGit/GTRepository+Status.h new file mode 100644 index 000000000..86d4121ca --- /dev/null +++ b/ObjectiveGit/GTRepository+Status.h @@ -0,0 +1,148 @@ +// +// GTRepository+Status.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 08/08/2013. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import "GTRepository.h" + +#import "git2/status.h" + +@class GTStatusDelta; + +NS_ASSUME_NONNULL_BEGIN + +/// An enum representing the status of a file +/// See git_status_t +typedef NS_OPTIONS(NSInteger, GTFileStatusFlags) { + GTFileStatusCurrent = GIT_STATUS_CURRENT, + + GTFileStatusNewInIndex = GIT_STATUS_INDEX_NEW, + GTFileStatusModifiedInIndex = GIT_STATUS_INDEX_MODIFIED, + GTFileStatusDeletedInIndex = GIT_STATUS_INDEX_DELETED, + GTFileStatusRenamedInIndex = GIT_STATUS_INDEX_RENAMED, + GTFileStatusTypeChangedInIndex = GIT_STATUS_INDEX_TYPECHANGE, + + GTFileStatusNewInWorktree = GIT_STATUS_WT_NEW, + GTFileStatusModifiedInWorktree = GIT_STATUS_WT_MODIFIED, + GTFileStatusDeletedInWorktree = GIT_STATUS_WT_DELETED, + GTFileStatusTypeChangedInWorktree = GIT_STATUS_WT_TYPECHANGE, + GTFileStatusRenamedInWorktree = GIT_STATUS_WT_RENAMED, + + GTFileStatusIgnored = GIT_STATUS_IGNORED, +}; + +/// An `NSNumber` wrapped `GTRepositoryStatusOptionsShow` bitmask. +/// +/// For extending the reporting of status. Using the flags documented below this +/// decides what files are sent when enumerating the status. +extern NSString *const GTRepositoryStatusOptionsShowKey; + +/// An enum, for use as documented, with the `GTRepositoryStatusOptionsShowKey` +/// key. +/// +/// See status.h for documentation of each individual flag. +typedef enum { + GTRepositoryStatusOptionsShowIndexAndWorkingDirectory = GIT_STATUS_SHOW_INDEX_AND_WORKDIR, + GTRepositoryStatusOptionsShowIndexOnly = GIT_STATUS_SHOW_INDEX_ONLY, + GTRepositoryStatusOptionsShowWorkingDirectoryOnly = GIT_STATUS_SHOW_WORKDIR_ONLY, +} GTRepositoryStatusOptionsShow; + +/// An `NSNumber` wrapped `GTRepositoryStatusOptionsFlags` bitmask containing +/// any of the flags documented below. +extern NSString *const GTRepositoryStatusOptionsFlagsKey; + +/// An enum, for use as documented, with the `GTRepositoryStatusOptionsFlagsKey` +/// key. +/// +/// See status.h for documentation of each individual flag. +typedef enum { + GTRepositoryStatusFlagsIncludeUntracked = GIT_STATUS_OPT_INCLUDE_UNTRACKED, + GTRepositoryStatusFlagsIncludeIgnored = GIT_STATUS_OPT_INCLUDE_IGNORED, + GTRepositoryStatusFlagsIncludeUnmodified = GIT_STATUS_OPT_INCLUDE_UNMODIFIED, + GTRepositoryStatusFlagsExcludeSubmodules = GIT_STATUS_OPT_EXCLUDE_SUBMODULES, + GTRepositoryStatusFlagsRecurseUntrackedDirectories = GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS, + GTRepositoryStatusFlagsDisablePathspecMatch = GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH, + GTRepositoryStatusFlagsRecurseIgnoredDirectories = GIT_STATUS_OPT_RECURSE_IGNORED_DIRS, + GTRepositoryStatusFlagsRenamesHeadToIndex = GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX, + GTRepositoryStatusFlagsRenamesIndexToWorkingDirectory = GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR, + GTRepositoryStatusFlagsRenamesFromRewrites = GIT_STATUS_OPT_RENAMES_FROM_REWRITES, + GTRepositoryStatusFlagsSortCaseSensitively = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY, + GTRepositoryStatusFlagsSortCaseInsensitively = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY, + GTRepositoryStatusFlagsIncludeUnreadable = GIT_STATUS_OPT_INCLUDE_UNREADABLE, + GTRepositoryStatusFlagsIncludeUnreadableAsUntracked = GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED, +} GTRepositoryStatusFlags; + +/// An `NSArray` of `NSStrings`s to limit the status to specific paths inside +/// the repository. The entries in the array represent either single paths or +/// filename patterns with wildcard matching a la standard shell glob (see +/// http://linux.die.net/man/7/glob for wildcard matching rules). +/// +/// Defaults to including all files. +extern NSString *const GTRepositoryStatusOptionsPathSpecArrayKey; + +@interface GTRepository (Status) + +/// `YES` if the working directory has no modified, new, or deleted files. +@property (nonatomic, readonly, getter = isWorkingDirectoryClean) BOOL workingDirectoryClean; + +/// For each file in the repository, calls your block with the URL of the file +/// and the status of that file in the repository. +/// +/// This will show all file statuses unless a pathspec is specified in the +/// options dictionary (using the `GTRepositoryStatusOptionsPathSpecArrayKey` +/// key). +/// +/// options - A dictionary of options using the constants above +/// for keys. If no flags are passed in using +/// `GTRepositoryStatusOptionsFlagsKey` the defaults of +/// GTRepositoryStatusOptionsFlagsIncludeIgnored, +/// GTRepositoryStatusOptionsFlagsIncludeUntracked and +/// GTRepositoryStatusOptionsFlagsRecurseUntrackedDirectories +/// are used. +/// error - Will optionally be set in the event of a failure. +/// block - The block that gets called for each file. +/// `headToIndex` is the delta between the HEAD and +/// index. `indexToWorkingDirectory` is the same but +/// between the index and the working directory. If +/// `stop` is set to `YES`, the iteration will cease +/// after the current step. +/// Must not be nil. +/// +/// Returns `NO` in case of a failure or `YES` if the enumeration completed +/// successfully. +- (BOOL)enumerateFileStatusWithOptions:(NSDictionary * _Nullable)options error:(NSError **)error usingBlock:(void (^ _Nullable)(GTStatusDelta * _Nullable headToIndex, GTStatusDelta * _Nullable indexToWorkingDirectory, BOOL *stop))block; + +/// Query the status of one file +/// +/// filePath - A string path relative to the working copy. The must not be nil. +/// success - If not NULL, will be set to indicate success or fail. +/// error - If not nil, set to any error that occurs. +/// +/// Returns the combined GTFileStatusFlags for the file. +- (GTFileStatusFlags)statusForFile:(NSString *)filePath success:(BOOL * _Nullable)success error:(NSError **)error; + +/// Tests the ignore rules to see if the file should be considered as ignored. +/// +/// fileURL - A local file URL for a file in the repository. Must not be nil. +/// success - If not NULL, will be set to indicate success or fail. +/// error - If not nil, set to any error that occurs. +/// +/// Returns YES if the file should be ignored; NO otherwise. +- (BOOL)shouldFileBeIgnored:(NSURL *)fileURL success:(BOOL * _Nullable)success error:(NSError **)error; + +/// An enum for use with shouldIgnoreFileURL:error: below +typedef NS_ENUM(NSInteger, GTFileIgnoreState) { + GTFileIgnoreStateIgnoreCheckFailed = -1, + GTFileIgnoreStateShouldNotIgnore = 0, + GTFileIgnoreStateShouldIgnore = 1 +}; + +/// Convenience wrapper for shouldFileBeIgnored:success:error: +- (GTFileIgnoreState)shouldIgnoreFileURL:(NSURL *)fileURL error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTRepository+Status.m b/ObjectiveGit/GTRepository+Status.m similarity index 83% rename from Classes/GTRepository+Status.m rename to ObjectiveGit/GTRepository+Status.m index d46145cd1..7f2559134 100644 --- a/Classes/GTRepository+Status.m +++ b/ObjectiveGit/GTRepository+Status.m @@ -9,12 +9,13 @@ #import "GTRepository+Status.h" #import "GTConfiguration.h" #import "GTStatusDelta.h" - #import "NSError+Git.h" #import "NSArray+StringArray.h" #import "EXTScope.h" +#import "git2/errors.h" + NSString *const GTRepositoryStatusOptionsShowKey = @"GTRepositoryStatusOptionsShow"; NSString *const GTRepositoryStatusOptionsFlagsKey = @"GTRepositoryStatusOptionsFlags"; NSString *const GTRepositoryStatusOptionsPathSpecArrayKey = @"GTRepositoryStatusOptionsPathSpecArray"; @@ -67,23 +68,23 @@ - (BOOL)enumerateFileStatusWithOptions:(NSDictionary *)options error:(NSError ** - (BOOL)isWorkingDirectoryClean { __block BOOL clean = YES; [self enumerateFileStatusWithOptions:nil error:NULL usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { - GTStatusDeltaStatus headToIndexStatus = headToIndex.status; - GTStatusDeltaStatus indexToWorkDirStatus = indexToWorkingDirectory.status; + GTDeltaType headToIndexStatus = headToIndex.status; + GTDeltaType indexToWorkDirStatus = indexToWorkingDirectory.status; // first, have items been deleted? - if (indexToWorkDirStatus == GTStatusDeltaStatusDeleted || headToIndexStatus == GTStatusDeltaStatusDeleted) { + if (indexToWorkDirStatus == GTDeltaTypeDeleted || headToIndexStatus == GTDeltaTypeDeleted) { clean = NO; *stop = YES; } // any untracked files? - if (indexToWorkDirStatus == GTStatusDeltaStatusAdded) { + if (indexToWorkDirStatus == GTDeltaTypeUntracked) { clean = NO; *stop = YES; } // next, have items been modified? - if (indexToWorkDirStatus == GTStatusDeltaStatusModified || headToIndexStatus == GTStatusDeltaStatusModified) { + if (indexToWorkDirStatus == GTDeltaTypeModified || headToIndexStatus == GTDeltaTypeModified) { clean = NO; *stop = YES; } @@ -121,7 +122,16 @@ - (BOOL)shouldFileBeIgnored:(NSURL *)fileURL success:(BOOL *)success error:(NSEr } if (success != NULL) *success = YES; - return (ignoreState == 0 ? YES : NO); + return (ignoreState == 1 ? YES : NO); +} + +- (GTFileIgnoreState)shouldIgnoreFileURL:(NSURL *)fileURL error:(NSError **)error { + BOOL success = NO; + BOOL ignore = [self shouldFileBeIgnored:fileURL success:&success error:error]; + if (success) { + return (ignore ? GTFileIgnoreStateShouldIgnore : GTFileIgnoreStateShouldNotIgnore); + } + return GTFileIgnoreStateIgnoreCheckFailed; } @end diff --git a/ObjectiveGit/GTRepository.h b/ObjectiveGit/GTRepository.h new file mode 100644 index 000000000..b57430b50 --- /dev/null +++ b/ObjectiveGit/GTRepository.h @@ -0,0 +1,660 @@ +// +// GTRepository.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 2/17/11. +// +// The MIT License +// +// Copyright (c) 2011 Tim Clem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "GTBranch.h" +#import "GTEnumerator.h" +#import "GTFilterSource.h" +#import "GTObject.h" +#import "GTReference.h" +#import "GTFilterList.h" +#import "GTCheckoutOptions.h" +#import "git2/checkout.h" +#import "git2/repository.h" +#import "git2/transport.h" +#import "git2/sys/transport.h" + +@class GTBlob; +@class GTCommit; +@class GTConfiguration; +@class GTDiffFile; +@class GTIndex; +@class GTObjectDatabase; +@class GTOdbObject; +@class GTSignature; +@class GTSubmodule; +@class GTTag; +@class GTTree; +@class GTRemote; +@class GTNote; + +NS_ASSUME_NONNULL_BEGIN + +/// Transport flags sent as options to +cloneFromURL... method +typedef NS_OPTIONS(NSInteger, GTTransportFlags) { + GTTransportFlagsNone = GIT_TRANSPORTFLAGS_NONE +}; + +/// An `NSNumber` wrapped `GTTransportFlags`, documented above. +/// Default value is `GTTransportFlagsNone`. +extern NSString * const GTRepositoryCloneOptionsTransportFlags; + +/// An `NSNumber` wrapped `BOOL`, if YES, create a bare clone. +/// Default value is `NO`. +extern NSString * const GTRepositoryCloneOptionsBare; + +/// An `NSNumber` wrapped `BOOL`, if NO, don't checkout the remote HEAD. +/// Default value is `YES`. +extern NSString * const GTRepositoryCloneOptionsPerformCheckout; + +/// A `GTCheckoutOptions` object describing how to perform the checkout. +extern NSString * const GTRepositoryCloneOptionsCheckoutOptions; + +/// A `GTCredentialProvider`, that will be used to authenticate against the +/// remote. +extern NSString * const GTRepositoryCloneOptionsCredentialProvider; + +/// A BOOL indicating whether local clones should actually clone, or just link. +extern NSString * const GTRepositoryCloneOptionsCloneLocal; + +/// A NSURL pointing to a local file that contains PEM-encoded certificate chain. +extern NSString * const GTRepositoryCloneOptionsServerCertificateURL; + +/// Repository extended open control flags for +/// +initWithURL:flags:ceilingDirs:error:. +/// +/// See respository.h for documentation of each individual flag. +typedef NS_OPTIONS(NSInteger, GTRepositoryOpenFlags) { + GTRepositoryOpenNoSearch = GIT_REPOSITORY_OPEN_NO_SEARCH, + GTRepositoryOpenCrossFS = GIT_REPOSITORY_OPEN_CROSS_FS, + GTRepositoryOpenBare = GIT_REPOSITORY_OPEN_BARE, +}; + +/// Initialization flags associated with `GTRepositoryInitOptionsFlags` for +/// +initializeEmptyRepositoryAtFileURL:options:error:. +/// +/// See `git_repository_init_flag_t` for more information. +typedef NS_OPTIONS(NSInteger, GTRepositoryInitFlags) { + GTRepositoryInitBare = GIT_REPOSITORY_INIT_BARE, + GTRepositoryInitWithoutReinitializing = GIT_REPOSITORY_INIT_NO_REINIT, + GTRepositoryInitWithoutDotGit = GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, + GTRepositoryInitCreatingRepositoryDirectory = GIT_REPOSITORY_INIT_MKDIR, + GTRepositoryInitCreatingIntermediateDirectories = GIT_REPOSITORY_INIT_MKPATH, + GTRepositoryInitWithExternalTemplate = GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, + GTRepositoryInitWithRelativeGitLink = GIT_REPOSITORY_INIT_RELATIVE_GITLINK, +}; + +/// An `NSNumber` wrapping `GTRepositoryInitFlags` with which to initialize the +/// repository. +extern NSString * const GTRepositoryInitOptionsFlags; + +/// An `NSNumber` wrapping a `mode_t` or `git_repository_init_mode_t` to use +/// for the initialized repository. +extern NSString * const GTRepositoryInitOptionsMode; + +/// An `NSString` to the working directory that should be used. If this is a +/// relative path, it will be resolved against the repository path. +extern NSString * const GTRepositoryInitOptionsWorkingDirectoryPath; + +/// An `NSString` of the Git description to use for the new repository. +extern NSString * const GTRepositoryInitOptionsDescription; + +/// A file `NSURL` to the template directory that should be used instead of the +/// defaults, if the `GTRepositoryInitWithExternalTemplate` flag is specified. +extern NSString * const GTRepositoryInitOptionsTemplateURL; + +/// An `NSString` of the name to use for the initial `HEAD` reference. +extern NSString * const GTRepositoryInitOptionsInitialHEAD; + +/// An `NSString` representing an origin URL to add to the repository after +/// initialization. +extern NSString * const GTRepositoryInitOptionsOriginURLString; + +/// The possible states for the repository to be in, based on the current ongoing operation. +typedef NS_ENUM(NSInteger, GTRepositoryStateType) { + GTRepositoryStateNone = GIT_REPOSITORY_STATE_NONE, + GTRepositoryStateMerge = GIT_REPOSITORY_STATE_MERGE, + GTRepositoryStateRevert = GIT_REPOSITORY_STATE_REVERT, + GTRepositoryStateCherryPick = GIT_REPOSITORY_STATE_CHERRYPICK, + GTRepositoryStateBisect = GIT_REPOSITORY_STATE_BISECT, + GTRepositoryStateRebase = GIT_REPOSITORY_STATE_REBASE, + GTRepositoryStateRebaseInteractive = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE, + GTRepositoryStateRebaseMerge = GIT_REPOSITORY_STATE_REBASE_MERGE, + GTRepositoryStateApplyMailbox = GIT_REPOSITORY_STATE_APPLY_MAILBOX, + GTRepositoryStateApplyMailboxOrRebase = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE, +}; + +@interface GTRepository : NSObject + +/// The file URL for the repository's working directory. +/// Returns nil for a bare repository. +@property (nonatomic, readonly, strong) NSURL * _Nullable fileURL; +/// The file URL for the repository's .git directory. +@property (nonatomic, readonly, strong) NSURL * _Nullable gitDirectoryURL; + +/// Is this a bare repository (one without a working directory)? +@property (nonatomic, readonly, getter = isBare) BOOL bare; + +/// Is this an empty (freshly initialized) repository? +@property (nonatomic, readonly, getter = isEmpty) BOOL empty; + +/// Is HEAD detached (not pointing to a branch or tag)? +@property (nonatomic, readonly, getter = isHEADDetached) BOOL HEADDetached; + +/// Is HEAD unborn (pointing to a branch without an initial commit)? +@property (nonatomic, readonly, getter = isHEADUnborn) BOOL HEADUnborn; + +/// Initializes a new repository at the given file URL. +/// +/// fileURL - The file URL for the new repository. Cannot be nil. +/// options - A dictionary of `GTRepositoryInitOptions…` keys controlling how +/// the repository is initialized, or nil to use the defaults. +/// error - The error if one occurs. +/// +/// Returns the initialized repository, or nil if an error occurred. ++ (instancetype _Nullable)initializeEmptyRepositoryAtFileURL:(NSURL *)fileURL options:(NSDictionary * _Nullable)options error:(NSError **)error; + +/// Convenience class initializer which uses the default options. +/// +/// localFileURL - The file URL for the new repository. Cannot be nil. +/// error - The error if one occurs. +/// +/// Returns the initialized repository, or nil if an error occurred. ++ (instancetype _Nullable)repositoryWithURL:(NSURL *)localFileURL error:(NSError **)error; + +/// Convenience initializer which uses the default options. +/// +/// localFileURL - The file URL for the new repository. Cannot be nil. +/// error - The error if one occurs. +/// +/// Returns the initialized repository, or nil if an error occurred. +- (instancetype _Nullable)initWithURL:(NSURL *)localFileURL error:(NSError **)error; + +/// Convenience initializer to find and open a repository with extended controls. +/// +/// localFileURL - The file URL for the new repository. Cannot be nil. +/// flags - A combination of the `GTRepositoryOpenFlags` flags. +/// ceilingDirURLs - An array of URLs at which the search for a containing +/// repository should terminate. Can be NULL. +/// error - The error if one occurs. +/// +/// Returns the initialized repository, or nil if an error occurred. +- (instancetype _Nullable)initWithURL:(NSURL *)localFileURL flags:(NSInteger)flags ceilingDirs:(NSArray * _Nullable)ceilingDirURLs error:(NSError **)error; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver to wrap the given repository object. Designated initializer. +/// +/// repository - The repository to wrap. The receiver will take over memory +/// management of this object, so it must not be freed elsewhere +/// after this method is invoked. This must not be nil. +/// +/// Returns an initialized GTRepository, or nil if an erroe occurred. +- (instancetype _Nullable)initWithGitRepository:(git_repository *)repository NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_repository` object. +- (git_repository *)git_repository __attribute__((objc_returns_inner_pointer)); + +/// Clone a repository +/// +/// originURL - The URL to clone from. Must not be nil. +/// workdirURL - A URL to the desired working directory on the local machine. Must not be nil. +/// options - A dictionary consisting of the options: +/// `GTRepositoryCloneOptionsTransportFlags`, +/// `GTRepositoryCloneOptionsBare`, +/// `GTRepositoryCloneOptionsPerformCheckout`, +/// `GTRepositoryCloneOptionsCheckoutOptions`, +/// `GTRepositoryCloneOptionsCredentialProvider`, +/// `GTRepositoryCloneOptionsCloneLocal`, +/// `GTRepositoryCloneOptionsServerCertificateURL` +/// error - A pointer to fill in case of trouble. +/// transferProgressBlock - This block is called with network transfer updates. +/// May be NULL. +/// May be NULL. +/// +/// returns nil (and fills the error parameter) if an error occurred, or a GTRepository object if successful. ++ (instancetype _Nullable)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL options:(NSDictionary * _Nullable)options error:(NSError **)error transferProgressBlock:(void (^ _Nullable)(const git_transfer_progress *, BOOL *stop))transferProgressBlock; + +/// Lookup objects in the repo by oid or sha1 +- (id _Nullable)lookUpObjectByOID:(GTOID *)oid objectType:(GTObjectType)type error:(NSError **)error; +- (id _Nullable)lookUpObjectByOID:(GTOID *)oid error:(NSError **)error; +- (id _Nullable)lookUpObjectBySHA:(NSString *)sha objectType:(GTObjectType)type error:(NSError **)error; +- (id _Nullable)lookUpObjectBySHA:(NSString *)sha error:(NSError **)error; + +/// Lookup an object in the repo using a revparse spec +- (id _Nullable)lookUpObjectByRevParse:(NSString *)spec error:(NSError **)error; + +/// Finds the branch with the given name and type. +/// +/// branchName - The name of the branch to look up (e.g., `master` or +/// `origin/master`). This must not be nil. +/// branchType - Whether the branch to look up is local or remote. +/// success - If not NULL, set to whether the branch lookup finished without +/// any errors. This can be `YES` even if no matching branch is +/// found. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the matching branch, or nil if no match was found or an error occurs. +/// The latter two cases can be distinguished by checking `success`. +- (GTBranch * _Nullable)lookUpBranchWithName:(NSString *)branchName type:(GTBranchType)branchType success:(BOOL * _Nullable)success error:(NSError **)error; + +/// List all references in the repository +/// +/// repository - The GTRepository to list references in +/// error(out) - will be filled if an error occurs +/// +/// returns an array of NSStrings holding the names of the references +/// returns nil if an error occurred and fills the error parameter +- (NSArray * _Nullable)referenceNamesWithError:(NSError **)error; + +/// Get the HEAD reference. +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns a GTReference or nil if an error occurs. +- (GTReference * _Nullable)headReferenceWithError:(NSError **)error; + +/// Move HEAD reference safely, since deleting and recreating HEAD is always wrong. +/// +/// reference - The new target reference for HEAD. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns NO if an error occurs. +- (BOOL)moveHEADToReference:(GTReference *)reference error:(NSError **)error; + +/// Move HEAD reference safely, since deleting and recreating HEAD is always wrong. +/// +/// commit - The commit which HEAD should point to. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns NO if an error occurs. +- (BOOL)moveHEADToCommit:(GTCommit *)commit error:(NSError **)error; + +/// Get the local branches. +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns an array of GTBranches or nil if an error occurs. +- (NSArray * _Nullable)localBranchesWithError:(NSError **)error; + +/// Get the remote branches. +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns an array of GTBranches or nil if an error occurs. +- (NSArray * _Nullable)remoteBranchesWithError:(NSError **)error; + +/// Get branches with names sharing a given prefix. +/// +/// prefix - The prefix to use for filtering. Must not be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns an array of GTBranches or nil if an error occurs. +- (NSArray * _Nullable)branchesWithPrefix:(NSString *)prefix error:(NSError **)error; + +/// Get the local and remote branches and merge them together by combining local +/// branches with their remote branch, if they have one. +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns an array of GTBranches or nil if an error occurs. +- (NSArray * _Nullable)branches:(NSError **)error; + +/// List all remotes in the repository +/// +/// error - will be filled if an error occurs +/// +/// returns an array of NSStrings holding the names of the remotes, or nil if an error occurred +- (NSArray * _Nullable)remoteNamesWithError:(NSError **)error; + +/// Delete the given remote by name +/// +/// error - If not NULL, set to any error that occurs. +/// +/// returns YES if the deletion succeeded, otherwise NO. +- (BOOL)deleteRemoteNamed:(NSString *)remoteName error:(NSError **)error; + +/// Get all tags in the repository. +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns an array of GTTag or nil if an error occurs. +- (NSArray * _Nullable)allTagsWithError:(NSError **)error; + +/// Count all commits in the current branch (HEAD) +/// +/// error(out) - will be filled if an error occurs +/// +/// returns number of commits in the current branch or NSNotFound if an error occurred +- (NSUInteger)numberOfCommitsInCurrentBranch:(NSError **)error; + +/// Creates a direct reference to the given OID. +/// +/// name - The full name for the new reference. This must not be nil. +/// targetOID - The OID that the new ref should point to. This must not be nil. +/// message - A message to use when creating the reflog entry for this action. +/// This may be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the created ref, or nil if an error occurred. +- (GTReference * _Nullable)createReferenceNamed:(NSString *)name fromOID:(GTOID *)targetOID message:(NSString * _Nullable)message error:(NSError **)error; + +/// Creates a symbolic reference to another ref. +/// +/// name - The full name for the new reference. This must not be nil. +/// targetRef - The ref that the new ref should point to. This must not be nil. +/// message - A message to use when creating the reflog entry for this action. +/// This may be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the created ref, or nil if an error occurred. +- (GTReference * _Nullable)createReferenceNamed:(NSString *)name fromReference:(GTReference *)targetRef message:(NSString * _Nullable)message error:(NSError **)error; + +/// Create a new local branch pointing to the given OID. +/// +/// name - The name for the new branch (e.g., `master`). This must not be +/// nil. +/// targetOID - The OID to create the new branch off. This must not be nil. +/// message - A message to use when creating the reflog entry for this action. +/// This may be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the new branch, or nil if an error occurred. +- (GTBranch * _Nullable)createBranchNamed:(NSString *)name fromOID:(GTOID *)targetOID message:(NSString * _Nullable)message error:(NSError **)error; + +/// Get the current branch. +/// +/// error(out) - will be filled if an error occurs +/// +/// returns the current branch or nil if an error occurred. +- (GTBranch * _Nullable)currentBranchWithError:(NSError **)error; + +/// Find the commits that are on our local branch but not on the remote branch. +/// +/// remoteBranch - The remote branch to use as a reference. Must not be nil. +/// error(out) - will be filled if an error occurs +/// +/// returns the local commits, an empty array if there is no remote branch, or nil if an error occurred +- (NSArray * _Nullable)localCommitsRelativeToRemoteBranch:(GTBranch *)remoteBranch error:(NSError **)error; + +/// Retrieves git's "prepared message" for the next commit, like the default +/// message pre-filled when committing after a conflicting merge. +/// +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the message from disk, or nil if no prepared message exists or an +/// error occurred. +- (NSString * _Nullable)preparedMessageWithError:(NSError **)error; + +/// The signature for the user at the current time, based on the repository and +/// system configs. If the user's name or email have not been set, reasonable +/// defaults will be used instead. Will never return nil. +/// +/// Returns the signature. +- (GTSignature * _Nullable)userSignatureForNow; + +/// Enumerates over all the tracked submodules in the repository. +/// +/// recursive - Whether to recurse into nested submodules, depth-first. +/// block - A block to execute for each `submodule` found. If an error +/// occurred while reading the submodule, `submodule` will be nil and +/// `error` will contain the error information. Setting `stop` to YES +/// will cause enumeration to stop after the block returns. This must +/// not be nil. +- (void)enumerateSubmodulesRecursively:(BOOL)recursive usingBlock:(void (^)(GTSubmodule * _Nullable submodule, NSError *error, BOOL *stop))block; + +/// Looks up the top-level submodule with the given name. This will not recurse +/// into submodule repositories. +/// +/// name - The name of the submodule. This must not be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the first submodule that matches the given name, or nil if an error +/// occurred locating or instantiating the GTSubmodule. +- (GTSubmodule * _Nullable)submoduleWithName:(NSString *)name error:(NSError **)error; + +/// Finds the merge base between the commits pointed at by the given OIDs. +/// +/// firstOID - The OID for the first commit. This must not be nil. +/// secondOID - The OID for the second commit. This must not be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the merge base, or nil if none is found or an error occurred. +- (GTCommit * _Nullable)mergeBaseBetweenFirstOID:(GTOID *)firstOID secondOID:(GTOID *)secondOID error:(NSError **)error; + +/// The object database backing the repository. +/// +/// error - The error if one occurred. +/// +/// Returns the object database, or nil if an error occurred. +- (GTObjectDatabase * _Nullable)objectDatabaseWithError:(NSError **)error; + +/// The configuration for the repository. +/// +/// error - The error if one occurred. +/// +/// Returns the configuration, or nil if an error occurred. +- (GTConfiguration * _Nullable)configurationWithError:(NSError **)error; + +/// The index for the repository. +/// +/// error - The error if one occurred. +/// +/// Returns the index, or nil if an error occurred. +- (GTIndex * _Nullable)indexWithError:(NSError **)error; + +/// Creates a new lightweight tag in this repository. +/// +/// name - Name for the tag; this name is validated +/// for consistency. It should also not conflict with an +/// already existing tag name. Must not be nil. +/// target - Object to which this tag points. This object +/// must belong to this repository. Must not be nil. +/// error - Will be filled with a NSError instance on failuer. +/// May be NULL. +/// +/// Returns YES on success or NO otherwise. +- (BOOL)createLightweightTagNamed:(NSString *)tagName target:(GTObject *)target error:(NSError **)error; + +/// Creates an annotated tag in this repo. Existing tags are not overwritten. +/// +/// tagName - Name for the tag; this name is validated +/// for consistency. It should also not conflict with an +/// already existing tag name +/// theTarget - Object to which this tag points. This object +/// must belong to this repository. +/// tagger - Signature of the tagger for this tag, and +/// of the tagging time +/// message - Full message for this tag +/// error - Will be filled with a NSError object in case of error. +/// May be NULL. +/// +/// Returns the object ID of the newly created tag or nil on error. +- (GTOID * _Nullable)OIDByCreatingTagNamed:(NSString *)tagName target:(GTObject *)theTarget tagger:(GTSignature *)theTagger message:(NSString *)theMessage error:(NSError **)error; + +/// Creates an annotated tag in this repo. Existing tags are not overwritten. +/// +/// tagName - Name for the tag; this name is validated +/// for consistency. It should also not conflict with an +/// already existing tag name +/// theTarget - Object to which this tag points. This object +/// must belong to this repository. +/// tagger - Signature of the tagger for this tag, and +/// of the tagging time +/// message - Full message for this tag +/// error - Will be filled with a NSError object in case of error. +/// May be NULL. +/// +/// Returns the newly created tag or nil on error. +- (GTTag * _Nullable)createTagNamed:(NSString *)tagName target:(GTObject *)theTarget tagger:(GTSignature *)theTagger message:(NSString *)theMessage error:(NSError **)error; + +/// Checkout a commit +/// +/// targetCommit - The commit to checkout. Must not be nil. +/// options - The checkout options to use. Can be nil. +/// error - The error if one occurred. Can be NULL. +/// +/// Returns YES if operation was successful, NO otherwise +- (BOOL)checkoutCommit:(GTCommit *)targetCommit options:(GTCheckoutOptions * _Nullable)options error:(NSError **)error; + +/// Checkout a reference +/// +/// targetReference - The reference to checkout. Must not be nil. +/// options - The checkout options to use. Can be nil. +/// error - The error if one occurred. Can be NULL. +/// +/// Returns YES if operation was successful, NO otherwise +- (BOOL)checkoutReference:(GTReference *)targetReference options:(GTCheckoutOptions * _Nullable)options error:(NSError **)error; + +/// Checkout an index +/// +/// index - The index to checkout. Must not be nil. +/// options - The checkout options to use. Can be nil. +/// error - The error if one occurred. Can be NULL. +/// +/// Returns YES if operation was successful, NO otherwise +- (BOOL)checkoutIndex:(GTIndex *)index options:(GTCheckoutOptions * _Nullable)options error:(NSError **)error; + +/// Checkout a tree +/// +/// targetTree - The tree to checkout. +/// options - The checkout options to use. Can be nil. +/// error - The error if one occurred. Can be NULL. +/// +/// Returns YES if operation was successful, NO otherwise +/// Note: this operation will NOT update HEAD to newly checked out tree. +- (BOOL)checkoutTree:(GTTree *)targetTree options:(GTCheckoutOptions * _Nullable)options error:(NSError **)error; + +/// Flush the gitattributes cache. +- (void)flushAttributesCache; + +/// Loads the filter list for a given path in the repository. +/// +/// path - The path to load filters for. This is used to determine which +/// filters to apply, and does not necessarily need to point to a file +/// that already exists. This must not be nil. +/// blob - The blob to which the filter will be applied, if known. This is +/// used to determine which filters to apply, and can differ from the +/// content of the file at `path`. This may be nil. +/// mode - The direction in which the data will be filtered. +/// options - The list options. See the libgit2 header for more information. +/// success - If not NULL, set to `NO` if an error occurs. If `nil` is +/// returned and this argument is set to `YES`, there were no filters +/// to apply. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns the loaded filter list, or nil if an error occurs or there are no +/// filters to apply to the given path. The latter two cases can be +/// distinguished using the value of `success`. +- (GTFilterList * _Nullable)filterListWithPath:(NSString *)path blob:(GTBlob * _Nullable)blob mode:(GTFilterSourceMode)mode options:(GTFilterListOptions)options success:(BOOL * _Nullable)success error:(NSError **)error; + +/// Calculates how far ahead/behind the commit represented by `headOID` is, +/// relative to the commit represented by `baseOID`. +/// +/// ahead - Must not be NULL. +/// behind - Must not be NULL. +/// headOID - Must not be nil. +/// baseOID - Must not be nil. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns whether `ahead` and `behind` were successfully calculated. +- (BOOL)calculateAhead:(size_t *)ahead behind:(size_t *)behind ofOID:(GTOID *)headOID relativeToOID:(GTOID *)baseOID error:(NSError **)error; + +/// Creates an enumerator for walking the unique commits, as determined by a +/// pushing a starting OID and hiding the relative OID. +/// +/// fromOID - The starting OID. +/// relativeOID - The OID to hide. +/// error - The error if one occurred. +/// +/// Returns the enumerator or nil if an error occurred. +- (GTEnumerator * _Nullable)enumeratorForUniqueCommitsFromOID:(GTOID *)fromOID relativeToOID:(GTOID *)relativeOID error:(NSError **)error; + +/// Determines the status of a git repository--i.e., whether an operation +/// (merge, cherry-pick, etc) is in progress. +/// +/// state - A pointer to set the retrieved state. Must not be NULL. +/// error - The error if one occurred. +/// +/// Returns YES if operation was successful, NO otherwise +- (BOOL)calculateState:(GTRepositoryStateType *)state withError:(NSError **)error; + +/// Remove all the metadata associated with an ongoing command like merge, +/// revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc. +/// +/// error - The error if one occurred. +/// +/// Returns YES if operation was successful, NO otherwise +- (BOOL)cleanupStateWithError:(NSError * _Nullable __autoreleasing *)error; + +/// Creates a new note in this repo (using a default notes reference, e.g. "refs/notes/commits") +/// +/// note - Note text. +/// theTarget - Object (usually a commit) to which this note refers to. +/// This object must belong to this repository. +/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits") +/// author - Signature of the author for this note, and +/// of the note creation time +/// committer - Signature of the committer for this note. +/// overwrite - If set to YES, the note will be overwritten if it already exists. +/// error - Will be filled with a NSError object in case of error. +/// May be NULL. +/// +/// Returns the newly created note or nil on error. +- (GTNote * _Nullable)createNote:(NSString *)note target:(GTObject *)theTarget referenceName:(NSString * _Nullable)referenceName author:(GTSignature *)author committer:(GTSignature *)committer overwriteIfExists:(BOOL)overwrite error:(NSError **)error; + +/// Removes a note attached to object in this repo +/// +/// parentObject - Object (usually a commit) to which the note to be removed is attached to. +/// This object must belong to this repository. +/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits") +/// author - Signature of the author for this note removal, and +/// of the note removal time +/// committer - Signature of the committer for this note removal. +/// error - Will be filled with a NSError object in case of error. +/// May be NULL. +/// +/// Returns the YES on success and NO on error. +- (BOOL)removeNoteFromObject:(GTObject *)parentObject referenceName:(NSString * _Nullable)referenceName author:(GTSignature *)author committer:(GTSignature *)committer error:(NSError **)error; + +/// Enumerates through all stored notes in this repo +/// +/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits") +/// error - Will be filled with a NSError object in case of error. +/// May be NULL. +/// block - A block to be called on each encountered note object. The block accepts +/// a reference to `note`, an `object` that is annotated with the note. +/// If the block sets `stop` to YES, the iterator is finished. +/// +/// Returns YES on overall success or NO on error of any kind. +- (BOOL)enumerateNotesWithReferenceName:(NSString * _Nullable)referenceName error:(NSError **)error usingBlock:(void (^)(GTNote * _Nullable note, GTObject * _Nullable object, NSError * _Nullable error, BOOL *stop))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTRepository.m b/ObjectiveGit/GTRepository.m similarity index 54% rename from Classes/GTRepository.m rename to ObjectiveGit/GTRepository.m index ac87092ee..133216cd4 100644 --- a/Classes/GTRepository.m +++ b/ObjectiveGit/GTRepository.m @@ -31,6 +31,7 @@ #import "GTBlob.h" #import "GTBranch.h" +#import "GTCheckoutOptions.h" #import "GTCommit.h" #import "GTConfiguration+Private.h" #import "GTConfiguration.h" @@ -51,11 +52,27 @@ #import "NSArray+StringArray.h" #import "NSError+Git.h" #import "NSString+Git.h" - -NSString *const GTRepositoryCloneOptionsBare = @"GTRepositoryCloneOptionsBare"; -NSString *const GTRepositoryCloneOptionsCheckout = @"GTRepositoryCloneOptionsCheckout"; -NSString *const GTRepositoryCloneOptionsTransportFlags = @"GTRepositoryCloneOptionsTransportFlags"; -NSString *const GTRepositoryCloneOptionsCredentialProvider = @"GTRepositoryCloneOptionsCredentialProvider"; +#import "GTRepository+References.h" +#import "GTNote.h" + +#import "EXTScope.h" + +#import "git2.h" + +NSString * const GTRepositoryCloneOptionsBare = @"GTRepositoryCloneOptionsBare"; +NSString * const GTRepositoryCloneOptionsPerformCheckout = @"GTRepositoryCloneOptionsPerformCheckout"; +NSString * const GTRepositoryCloneOptionsCheckoutOptions = @"GTRepositoryCloneOptionsCheckoutOptions"; +NSString * const GTRepositoryCloneOptionsTransportFlags = @"GTRepositoryCloneOptionsTransportFlags"; +NSString * const GTRepositoryCloneOptionsCredentialProvider = @"GTRepositoryCloneOptionsCredentialProvider"; +NSString * const GTRepositoryCloneOptionsCloneLocal = @"GTRepositoryCloneOptionsCloneLocal"; +NSString * const GTRepositoryCloneOptionsServerCertificateURL = @"GTRepositoryCloneOptionsServerCertificateURL"; +NSString * const GTRepositoryInitOptionsFlags = @"GTRepositoryInitOptionsFlags"; +NSString * const GTRepositoryInitOptionsMode = @"GTRepositoryInitOptionsMode"; +NSString * const GTRepositoryInitOptionsWorkingDirectoryPath = @"GTRepositoryInitOptionsWorkingDirectoryPath"; +NSString * const GTRepositoryInitOptionsDescription = @"GTRepositoryInitOptionsDescription"; +NSString * const GTRepositoryInitOptionsTemplateURL = @"GTRepositoryInitOptionsTemplateURL"; +NSString * const GTRepositoryInitOptionsInitialHEAD = @"GTRepositoryInitOptionsInitialHEAD"; +NSString * const GTRepositoryInitOptionsOriginURLString = @"GTRepositoryInitOptionsOriginURLString"; typedef void (^GTRepositorySubmoduleEnumerationBlock)(GTSubmodule *submodule, NSError *error, BOOL *stop); typedef void (^GTRepositoryTagEnumerationBlock)(GTTag *tag, BOOL *stop); @@ -79,6 +96,9 @@ @interface GTRepository () @implementation GTRepository - (NSString *)description { + if (self.isBare) { + return [NSString stringWithFormat:@"<%@: %p> (bare) gitDirectoryURL: %@", self.class, self, self.gitDirectoryURL]; + } return [NSString stringWithFormat:@"<%@: %p> fileURL: %@", self.class, self, self.fileURL]; } @@ -96,34 +116,28 @@ - (void)dealloc { #pragma mark API -+ (BOOL)isAGitDirectory:(NSURL *)directory { - NSFileManager *fm = [[NSFileManager alloc] init]; - BOOL isDir = NO; - NSURL *headFileURL = [directory URLByAppendingPathComponent:@"HEAD"]; - - if ([fm fileExistsAtPath:headFileURL.path isDirectory:&isDir] && !isDir) { - NSURL *objectsDir = [directory URLByAppendingPathComponent:@"objects"]; - if ([fm fileExistsAtPath:objectsDir.path isDirectory:&isDir] && isDir) { - return YES; - } - } - - return NO; -} - -+ (instancetype)initializeEmptyRepositoryAtFileURL:(NSURL *)localFileURL error:(NSError **)error { - return [self initializeEmptyRepositoryAtFileURL:localFileURL bare:NO error:error]; -} - -+ (instancetype)initializeEmptyRepositoryAtFileURL:(NSURL *)localFileURL bare:(BOOL)bare error:(NSError **)error { ++ (instancetype)initializeEmptyRepositoryAtFileURL:(NSURL *)localFileURL options:(NSDictionary *)optionsDict error:(NSError **)error { if (!localFileURL.isFileURL || localFileURL.path == nil) { if (error != NULL) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnsupportedSchemeError userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid file path URL to initialize repository.", @"") }]; - return NO; + return nil; } + git_repository_init_options options = GIT_REPOSITORY_INIT_OPTIONS_INIT; + options.mode = (uint32_t) + [optionsDict[GTRepositoryInitOptionsMode] unsignedIntegerValue]; + options.workdir_path = [optionsDict[GTRepositoryInitOptionsWorkingDirectoryPath] UTF8String]; + options.description = [optionsDict[GTRepositoryInitOptionsDescription] UTF8String]; + options.template_path = [optionsDict[GTRepositoryInitOptionsTemplateURL] path].UTF8String; + options.initial_head = [optionsDict[GTRepositoryInitOptionsInitialHEAD] UTF8String]; + options.origin_url = [optionsDict[GTRepositoryInitOptionsOriginURLString] UTF8String]; + + // This default mirrors git_repository_init(). + NSNumber *flags = optionsDict[GTRepositoryInitOptionsFlags]; + options.flags = (flags == nil ? GIT_REPOSITORY_INIT_MKPATH : (uint32_t)flags.unsignedIntegerValue); + const char *path = localFileURL.path.fileSystemRepresentation; git_repository *repository = NULL; - int gitError = git_repository_init(&repository, path, bare); + int gitError = git_repository_init_ext(&repository, path, &options); if (gitError != GIT_OK || repository == NULL) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to initialize empty repository at URL %@.", localFileURL]; return nil; @@ -132,11 +146,16 @@ + (instancetype)initializeEmptyRepositoryAtFileURL:(NSURL *)localFileURL bare:(B return [[self alloc] initWithGitRepository:repository]; } -+ (id)repositoryWithURL:(NSURL *)localFileURL error:(NSError **)error { ++ (instancetype)repositoryWithURL:(NSURL *)localFileURL error:(NSError **)error { return [[self alloc] initWithURL:localFileURL error:error]; } -- (id)initWithGitRepository:(git_repository *)repository { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithGitRepository:(git_repository *)repository { NSParameterAssert(repository != nil); self = [super init]; @@ -147,7 +166,7 @@ - (id)initWithGitRepository:(git_repository *)repository { return self; } -- (id)initWithURL:(NSURL *)localFileURL error:(NSError **)error { +- (instancetype)initWithURL:(NSURL *)localFileURL error:(NSError **)error { if (![localFileURL isFileURL] || localFileURL.path == nil) { if (error != NULL) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnsupportedSchemeError userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid file path URL to open.", @"") }]; return nil; @@ -163,64 +182,119 @@ - (id)initWithURL:(NSURL *)localFileURL error:(NSError **)error { return [self initWithGitRepository:r]; } +- (instancetype)initWithURL:(NSURL *)localFileURL flags:(NSInteger)flags ceilingDirs:(NSArray *)ceilingDirURLs error:(NSError **)error { + if (!localFileURL.isFileURL || localFileURL.path == nil) { + if (error != NULL) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileReadUnsupportedSchemeError userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid file path URL to open.", @"") }]; + return nil; + } + + // Concatenate URL paths. + NSMutableString *ceilingDirsString; + if (ceilingDirURLs.count > 0) { + ceilingDirsString = [[NSMutableString alloc] init]; + [ceilingDirURLs enumerateObjectsUsingBlock:^(NSURL * _Nonnull url, NSUInteger idx, BOOL * _Nonnull stop) { + if (idx < ceilingDirURLs.count - 1) { + [ceilingDirsString appendString:[NSString stringWithFormat:@"%@%c", url.path, GIT_PATH_LIST_SEPARATOR]]; + } else { + NSString *path = url.path; + NSAssert(path != nil, @"Unexpected nil path component"); + [ceilingDirsString appendString:path]; + } + }]; + } -typedef void(^GTTransferProgressBlock)(const git_transfer_progress *progress); + git_repository *r; + int gitError = git_repository_open_ext(&r, localFileURL.path.fileSystemRepresentation, (unsigned int)flags, ceilingDirsString.fileSystemRepresentation); + if (gitError < GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to open repository at URL %@.", localFileURL]; + return nil; + } -static void checkoutProgressCallback(const char *path, size_t completedSteps, size_t totalSteps, void *payload) { - if (payload == NULL) return; - void (^block)(NSString *, NSUInteger, NSUInteger) = (__bridge id)payload; - NSString *nsPath = (path != NULL ? [NSString stringWithUTF8String:path] : nil); - block(nsPath, completedSteps, totalSteps); + return [self initWithGitRepository:r]; } + +typedef void(^GTTransferProgressBlock)(const git_transfer_progress *progress, BOOL *stop); + static int transferProgressCallback(const git_transfer_progress *progress, void *payload) { if (payload == NULL) return 0; struct GTClonePayload *pld = payload; if (pld->transferProgressBlock == NULL) return 0; - pld->transferProgressBlock(progress); - return 0; + + BOOL stop = NO; + pld->transferProgressBlock(progress, &stop); + return (stop ? GIT_EUSER : 0); } struct GTClonePayload { - // credProvider must be first for compatibility with GTCredentialAcquireCallbackInfo - __unsafe_unretained GTCredentialProvider *credProvider; + GTCredentialAcquireCallbackInfo credProvider; __unsafe_unretained GTTransferProgressBlock transferProgressBlock; }; -+ (id)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL options:(NSDictionary *)options error:(NSError **)error transferProgressBlock:(void (^)(const git_transfer_progress *))transferProgressBlock checkoutProgressBlock:(void (^)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps))checkoutProgressBlock { +static int remoteCreate(git_remote **remote, git_repository *repo, const char *name, const char *url, void *payload) +{ + int error; + if ((error = git_remote_create(remote, repo, name, url)) < 0) + return error; + + return GIT_OK; +} + +struct GTRemoteCreatePayload { + git_remote_callbacks remoteCallbacks; +}; + ++ (instancetype _Nullable)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL options:(NSDictionary * _Nullable)options error:(NSError **)error transferProgressBlock:(void (^ _Nullable)(const git_transfer_progress *, BOOL *stop))transferProgressBlock { git_clone_options cloneOptions = GIT_CLONE_OPTIONS_INIT; NSNumber *bare = options[GTRepositoryCloneOptionsBare]; cloneOptions.bare = (bare == nil ? 0 : bare.boolValue); - NSNumber *transportFlags = options[GTRepositoryCloneOptionsTransportFlags]; - cloneOptions.ignore_cert_errors = (transportFlags == nil ? 0 : 1); + NSNumber *checkout = options[GTRepositoryCloneOptionsPerformCheckout]; + BOOL doCheckout = (checkout != nil ? [checkout boolValue] : YES); - NSNumber *checkout = options[GTRepositoryCloneOptionsCheckout]; - BOOL withCheckout = (checkout == nil ? YES : checkout.boolValue); - - if (withCheckout) { - git_checkout_options checkoutOptions = GIT_CHECKOUT_OPTIONS_INIT; - checkoutOptions.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; - checkoutOptions.progress_cb = checkoutProgressCallback; - checkoutOptions.progress_payload = (__bridge void *)checkoutProgressBlock; - cloneOptions.checkout_opts = checkoutOptions; + GTCheckoutOptions *checkoutOptions = options[GTRepositoryCloneOptionsCheckoutOptions]; + if (checkoutOptions == nil && doCheckout) { + checkoutOptions = [GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategySafe]; } - struct GTClonePayload payload; - cloneOptions.remote_callbacks.version = GIT_REMOTE_CALLBACKS_VERSION; + if (checkoutOptions != nil) { + cloneOptions.checkout_opts = *(checkoutOptions.git_checkoutOptions); + } GTCredentialProvider *provider = options[GTRepositoryCloneOptionsCredentialProvider]; + struct GTClonePayload payload = { + .credProvider = {provider}, + }; + + git_fetch_options fetchOptions = GIT_FETCH_OPTIONS_INIT; + fetchOptions.callbacks.version = GIT_REMOTE_CALLBACKS_VERSION; + if (provider) { - payload.credProvider = provider; - cloneOptions.remote_callbacks.credentials = GTCredentialAcquireCallback; + fetchOptions.callbacks.credentials = GTCredentialAcquireCallback; } payload.transferProgressBlock = transferProgressBlock; - cloneOptions.remote_callbacks.transfer_progress = transferProgressCallback; - cloneOptions.remote_callbacks.payload = &payload; + fetchOptions.callbacks.transfer_progress = transferProgressCallback; + fetchOptions.callbacks.payload = &payload; + cloneOptions.fetch_opts = fetchOptions; + cloneOptions.remote_cb = remoteCreate; + + BOOL localClone = [options[GTRepositoryCloneOptionsCloneLocal] boolValue]; + if (localClone) { + cloneOptions.local = GIT_CLONE_NO_LOCAL; + } + + NSURL *serverCertificateURL = options[GTRepositoryCloneOptionsServerCertificateURL]; + if (serverCertificateURL) { + int gitError = git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, serverCertificateURL.fileSystemRepresentation, NULL); + if (gitError < GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to configure the server certificate at %@", serverCertificateURL]; + return nil; + } + } // If our originURL is local, convert to a path before handing down. const char *remoteURL = NULL; @@ -243,17 +317,17 @@ + (id)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL opt - (id)lookUpObjectByGitOid:(const git_oid *)oid objectType:(GTObjectType)type error:(NSError **)error { git_object *obj; - int gitError = git_object_lookup(&obj, self.git_repository, oid, (git_otype)type); + int gitError = git_object_lookup(&obj, self.git_repository, oid, (git_object_t)type); if (gitError < GIT_OK) { if (error != NULL) { char oid_str[GIT_OID_HEXSZ+1]; git_oid_tostr(oid_str, sizeof(oid_str), oid); - *error = [NSError git_errorFor:gitError description:@"Failed to lookup object %s in repository.", oid_str]; + *error = [NSError git_errorFor:gitError description:@"Failed to lookup object" userInfo:@{GTGitErrorOID: [GTOID oidWithGitOid:oid]} failureReason:@"The object %s couldn't be found in the repository.", oid_str]; } return nil; } - return [GTObject objectWithObj:obj inRepository:self]; + return [GTObject objectWithObj:obj inRepository:self]; } - (id)lookUpObjectByGitOid:(const git_oid *)oid error:(NSError **)error { @@ -305,36 +379,77 @@ - (GTBranch *)lookUpBranchWithName:(NSString *)branchName type:(GTBranchType)bra if (ref == NULL) return nil; GTReference *gtRef = [[GTReference alloc] initWithGitReference:ref repository:self]; - return [[GTBranch alloc] initWithReference:gtRef repository:self]; + return [[GTBranch alloc] initWithReference:gtRef]; } - (GTReference *)headReferenceWithError:(NSError **)error { git_reference *headRef; int gitError = git_repository_head(&headRef, self.git_repository); if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get HEAD"]; + NSString *unborn = @""; + if (gitError == GIT_EUNBORNBRANCH) { + unborn = @" (unborn)"; + } + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get HEAD%@", unborn]; return nil; } return [[GTReference alloc] initWithGitReference:headRef repository:self]; } +typedef void (^GTRepositoryBranchEnumerationBlock)(GTBranch *branch, BOOL *stop); + +- (BOOL)enumerateBranchesWithType:(GTBranchType)type error:(NSError **)error usingBlock:(GTRepositoryBranchEnumerationBlock)block { + git_branch_iterator *iter = NULL; + git_reference *gitRef = NULL; + int gitError = git_branch_iterator_new(&iter, self.git_repository, (git_branch_t)type); + if (gitError != GIT_OK) { + if (error) *error = [NSError git_errorFor:gitError description:@"Branch enumeration failed"]; + return NO; + } + + @onExit { + git_branch_iterator_free(iter); + }; + + git_branch_t branchType; + while ((gitError = git_branch_next(&gitRef, &branchType, iter)) == GIT_OK) { + GTReference *ref = [[GTReference alloc] initWithGitReference:gitRef repository:self]; + GTBranch *branch = [GTBranch branchWithReference:ref]; + BOOL stop = NO; + block(branch, &stop); + if (stop) break; + } + + if (gitError != GIT_OK && gitError != GIT_ITEROVER) { + if (error) *error = [NSError git_errorFor:gitError description:@"Branch enumeration failed"]; + return NO; + } + + return YES; +} + - (NSArray *)localBranchesWithError:(NSError **)error { - return [self branchesWithPrefix:[GTBranch localNamePrefix] error:error]; + NSMutableArray *localBranches = [NSMutableArray array]; + BOOL success = [self enumerateBranchesWithType:GTBranchTypeLocal error:error usingBlock:^(GTBranch *branch, BOOL *stop) { + [localBranches addObject:branch]; + }]; + + if (success != YES) return nil; + + return [localBranches copy]; } - (NSArray *)remoteBranchesWithError:(NSError **)error { - NSArray *remoteBranches = [self branchesWithPrefix:[GTBranch remoteNamePrefix] error:error]; - if (remoteBranches == nil) return nil; + NSMutableArray *remoteBranches = [NSMutableArray array]; + BOOL success = [self enumerateBranchesWithType:GTBranchTypeRemote error:error usingBlock:^(GTBranch *branch, BOOL *stop) { + if (![branch.shortName isEqualToString:@"HEAD"]) + [remoteBranches addObject:branch]; + }]; - NSMutableArray *filteredList = [NSMutableArray arrayWithCapacity:remoteBranches.count]; - for (GTBranch *branch in remoteBranches) { - if (![branch.shortName isEqualToString:@"HEAD"]) { - [filteredList addObject:branch]; - } - } + if (success != YES) return nil; - return filteredList; + return [remoteBranches copy]; } - (NSArray *)branchesWithPrefix:(NSString *)prefix error:(NSError **)error { @@ -345,40 +460,60 @@ - (NSArray *)branchesWithPrefix:(NSString *)prefix error:(NSError **)error { for (NSString *refName in references) { if (![refName hasPrefix:prefix]) continue; - GTReference *ref = [[GTReference alloc] initByLookingUpReferenceNamed:refName inRepository:self error:error]; + GTReference *ref = [self lookUpReferenceWithName:refName error:error]; if (ref == nil) continue; - GTBranch *branch = [[GTBranch alloc] initWithReference:ref repository:self]; + GTBranch *branch = [[GTBranch alloc] initWithReference:ref]; if (branch == nil) continue; - + [branches addObject:branch]; } return branches; } -- (NSArray *)allBranchesWithError:(NSError **)error { - NSMutableArray *allBranches = [NSMutableArray array]; +- (NSArray *)branches:(NSError **)error { NSArray *localBranches = [self localBranchesWithError:error]; - NSArray *remoteBranches = [self remoteBranchesWithError:error]; - if (localBranches == nil || remoteBranches == nil) return nil; + if (localBranches == nil) return nil; - [allBranches addObjectsFromArray:localBranches]; + NSMutableArray *remoteBranches = [[self remoteBranchesWithError:error] mutableCopy]; + if (remoteBranches == nil) return nil; - // we want to add the remote branches that we don't already have as a local branch - NSMutableDictionary *shortNamesToBranches = [NSMutableDictionary dictionary]; + NSMutableArray *branches = [NSMutableArray array]; for (GTBranch *branch in localBranches) { - [shortNamesToBranches setObject:branch forKey:branch.shortName]; + GTBranch *trackingBranch = [branch trackingBranchWithError:NULL success:NULL]; + if (trackingBranch != nil) [remoteBranches removeObject:trackingBranch]; + [branches addObject:branch]; } - for (GTBranch *branch in remoteBranches) { - GTBranch *localBranch = [shortNamesToBranches objectForKey:branch.shortName]; - if (localBranch == nil) { - [allBranches addObject:branch]; - } + [branches addObjectsFromArray:remoteBranches]; + + return branches; +} + +- (NSArray *)remoteNamesWithError:(NSError **)error { + git_strarray array; + int gitError = git_remote_list(&array, self.git_repository); + if (gitError < GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to list all remotes."]; + return nil; } - return allBranches; + NSArray *remoteNames = [NSArray git_arrayWithStrarray:array]; + + git_strarray_free(&array); + + return remoteNames; +} + +- (BOOL)deleteRemoteNamed:(NSString *)remoteName error:(NSError **)error { + int gitError = git_remote_delete(self.git_repository, [remoteName cStringUsingEncoding:NSUTF8StringEncoding]); + if (gitError < GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to delete remote."]; + return NO; + } + + return YES; } struct GTRepositoryTagEnumerationInfo { @@ -391,7 +526,9 @@ static int GTRepositoryForeachTagCallback(const char *name, git_oid *oid, void * GTTag *tag = (GTTag *)[info->myself lookUpObjectByGitOid:oid objectType:GTObjectTypeTag error:NULL]; BOOL stop = NO; - info->block(tag, &stop); + if (tag != nil) { + info->block(tag, &stop); + } return stop ? GIT_EUSER : 0; } @@ -427,12 +564,12 @@ - (NSUInteger)numberOfCommitsInCurrentBranch:(NSError **)error { return [currentBranch numberOfCommitsWithError:error]; } -- (GTReference *)createReferenceNamed:(NSString *)name fromOID:(GTOID *)targetOID committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error { +- (GTReference *)createReferenceNamed:(NSString *)name fromOID:(GTOID *)targetOID message:(NSString *)message error:(NSError **)error { NSParameterAssert(name != nil); NSParameterAssert(targetOID != nil); git_reference *ref; - int gitError = git_reference_create(&ref, self.git_repository, name.UTF8String, targetOID.git_oid, 0, signature.git_signature, message.UTF8String); + int gitError = git_reference_create(&ref, self.git_repository, name.UTF8String, targetOID.git_oid, 0, message.UTF8String); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create direct reference to %@", targetOID]; return nil; @@ -441,13 +578,13 @@ - (GTReference *)createReferenceNamed:(NSString *)name fromOID:(GTOID *)targetOI return [[GTReference alloc] initWithGitReference:ref repository:self]; } -- (GTReference *)createReferenceNamed:(NSString *)name fromReference:(GTReference *)targetRef committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error { +- (GTReference *)createReferenceNamed:(NSString *)name fromReference:(GTReference *)targetRef message:(NSString *)message error:(NSError **)error { NSParameterAssert(name != nil); NSParameterAssert(targetRef != nil); NSParameterAssert(targetRef.name != nil); git_reference *ref; - int gitError = git_reference_symbolic_create(&ref, self.git_repository, name.UTF8String, targetRef.name.UTF8String, 0, signature.git_signature, message.UTF8String); + int gitError = git_reference_symbolic_create(&ref, self.git_repository, name.UTF8String, targetRef.name.UTF8String, 0, message.UTF8String); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create symbolic reference to %@", targetRef]; return nil; @@ -456,14 +593,14 @@ - (GTReference *)createReferenceNamed:(NSString *)name fromReference:(GTReferenc return [[GTReference alloc] initWithGitReference:ref repository:self]; } -- (GTBranch *)createBranchNamed:(NSString *)name fromOID:(GTOID *)targetOID committer:(GTSignature *)signature message:(NSString *)message error:(NSError **)error { +- (GTBranch *)createBranchNamed:(NSString *)name fromOID:(GTOID *)targetOID message:(NSString *)message error:(NSError **)error { NSParameterAssert(name != nil); NSParameterAssert(targetOID != nil); - GTReference *newRef = [self createReferenceNamed:[GTBranch.localNamePrefix stringByAppendingString:name] fromOID:targetOID committer:signature message:message error:error]; + GTReference *newRef = [self createReferenceNamed:[GTBranch.localNamePrefix stringByAppendingString:name] fromOID:targetOID message:message error:error]; if (newRef == nil) return nil; - return [GTBranch branchWithReference:newRef repository:self]; + return [GTBranch branchWithReference:newRef]; } - (BOOL)isEmpty { @@ -474,7 +611,7 @@ - (GTBranch *)currentBranchWithError:(NSError **)error { GTReference *head = [self headReferenceWithError:error]; if (head == nil) return nil; - return [GTBranch branchWithReference:head repository:self]; + return [GTBranch branchWithReference:head]; } - (NSArray *)localCommitsRelativeToRemoteBranch:(GTBranch *)remoteBranch error:(NSError **)error { @@ -500,18 +637,22 @@ - (NSArray *)referenceNamesWithError:(NSError **)error { } - (NSURL *)fileURL { - const char *path = git_repository_workdir(self.git_repository); + const char *cPath = git_repository_workdir(self.git_repository); // bare repository, you may be looking for gitDirectoryURL - if (path == NULL) return nil; + if (cPath == NULL) return nil; - return [NSURL fileURLWithPath:@(path) isDirectory:YES]; + NSString *path = @(cPath); + NSAssert(path, @"workdir is nil"); + return [NSURL fileURLWithPath:path isDirectory:YES]; } - (NSURL *)gitDirectoryURL { - const char *path = git_repository_path(self.git_repository); - if (path == NULL) return nil; + const char *cPath = git_repository_path(self.git_repository); + if (cPath == NULL) return nil; - return [NSURL fileURLWithPath:@(path) isDirectory:YES]; + NSString *path = @(cPath); + NSAssert(path, @"gitdirectory is nil"); + return [NSURL fileURLWithPath:path isDirectory:YES]; } - (BOOL)isBare { @@ -526,7 +667,7 @@ - (BOOL)isHEADUnborn { return (BOOL)git_repository_head_unborn(self.git_repository); } -- (NSString *)preparedMessageWithError:(NSError **)error { +- (NSString *)preparedMessageWithError:(NSError * __autoreleasing *)error { void (^setErrorFromCode)(int) = ^(int errorCode) { if (errorCode == 0 || errorCode == GIT_ENOTFOUND) { // Not an error. @@ -542,13 +683,13 @@ - (NSString *)preparedMessageWithError:(NSError **)error { int errorCode = git_repository_message(&msg, self.git_repository); if (errorCode != GIT_OK) { setErrorFromCode(errorCode); - git_buf_free(&msg); + git_buf_dispose(&msg); return nil; } NSString *message = [[NSString alloc] initWithBytes:msg.ptr length:msg.size encoding:NSUTF8StringEncoding]; - git_buf_free(&msg); + git_buf_dispose(&msg); return message; } @@ -563,7 +704,7 @@ - (GTCommit *)mergeBaseBetweenFirstOID:(GTOID *)firstOID secondOID:(GTOID *)seco if (error != NULL) *error = [NSError git_errorFor:errorCode description:@"Failed to find merge base between commits %@ and %@.", firstOID.SHA, secondOID.SHA]; return nil; } - + return [self lookUpObjectByGitOid:&mergeBase objectType:GTObjectTypeCommit error:error]; } @@ -587,7 +728,7 @@ - (GTIndex *)indexWithError:(NSError **)error { int gitError = git_repository_index(&index, self.git_repository); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get index for repository."]; - return NO; + return nil; } return [[GTIndex alloc] initWithGitIndex:index repository:self]; @@ -600,7 +741,9 @@ static int submoduleEnumerationCallback(git_submodule *git_submodule, const char NSError *error; // Use -submoduleWithName:error: so that we get a git_submodule that we own. - GTSubmodule *submodule = [info->parentRepository submoduleWithName:@(name) error:&error]; + NSString *submoduleName = @(name); + NSCAssert(submoduleName, @"submodule name is nil"); + GTSubmodule *submodule = [info->parentRepository submoduleWithName:submoduleName error:&error]; BOOL stop = NO; info->block(submodule, error, &stop); @@ -613,16 +756,6 @@ static int submoduleEnumerationCallback(git_submodule *git_submodule, const char return 0; } -- (BOOL)reloadSubmodules:(NSError **)error { - int gitError = git_submodule_reload_all(self.git_repository, 0); - if (gitError != GIT_OK) { - if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to reload submodules."]; - return NO; - } - - return YES; -} - - (void)enumerateSubmodulesRecursively:(BOOL)recursive usingBlock:(void (^)(GTSubmodule *submodule, NSError *error, BOOL *stop))block { NSParameterAssert(block != nil); @@ -652,24 +785,33 @@ - (GTSubmodule *)submoduleWithName:(NSString *)name error:(NSError **)error { #pragma mark User ++ (NSString *)defaultUserName { + NSString *name = NSFullUserName(); + if (name.length == 0) name = NSUserName(); + if (name.length == 0) name = @"nobody"; + return name; +} + ++ (NSString *)defaultEmail { + NSString *username = NSUserName(); + if (username.length == 0) username = @"nobody"; + NSString *domain = NSProcessInfo.processInfo.hostName ?: @"nowhere.local"; + return [NSString stringWithFormat:@"%@@%@", username, domain]; +} + - (GTSignature *)userSignatureForNow { GTConfiguration *configuration = [self configurationWithError:NULL]; NSString *name = [configuration stringForKey:@"user.name"]; - if (name.length == 0) { - name = NSFullUserName(); - if (name.length == 0) name = NSUserName(); - if (name.length == 0) name = @"nobody"; - } + if (name.length == 0) name = self.class.defaultUserName; NSString *email = [configuration stringForKey:@"user.email"]; - if (email == nil) { - NSString *username = NSUserName(); - if (username.length == 0) username = @"nobody"; - NSString *domain = NSProcessInfo.processInfo.hostName ?: @"nowhere.local"; - email = [NSString stringWithFormat:@"%@@%@", username, domain]; - } + if (email.length == 0) email = self.class.defaultEmail; + + NSDate *now = [NSDate date]; + GTSignature *signature = [[GTSignature alloc] initWithName:name email:email time:now]; + if (signature != nil) return signature; - return [[GTSignature alloc] initWithName:name email:email time:[NSDate date]]; + return [[GTSignature alloc] initWithName:self.class.defaultUserName email:self.class.defaultEmail time:now]; } #pragma mark Tagging @@ -706,84 +848,62 @@ - (GTTag *)createTagNamed:(NSString *)tagName target:(GTObject *)theTarget tagge #pragma mark Checkout -// The type of block passed to -checkout:strategy:progressBlock:notifyBlock:notifyFlags:error: for progress reporting -typedef void (^GTCheckoutProgressBlock)(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps); - -// The type of block passed to -checkout:strategy:progressBlock:notifyBlock:notifyFlags:error: for notification reporting -typedef int (^GTCheckoutNotifyBlock)(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir); - -static int checkoutNotifyCallback(git_checkout_notify_t why, const char *path, const git_diff_file *baseline, const git_diff_file *target, const git_diff_file *workdir, void *payload) { - if (payload == NULL) return 0; - GTCheckoutNotifyBlock block = (__bridge id)payload; - NSString *nsPath = (path != NULL ? @(path) : nil); - GTDiffFile *gtBaseline = (baseline != NULL ? [[GTDiffFile alloc] initWithGitDiffFile:*baseline] : nil); - GTDiffFile *gtTarget = (target != NULL ? [[GTDiffFile alloc] initWithGitDiffFile:*target] : nil); - GTDiffFile *gtWorkdir = (workdir != NULL ? [[GTDiffFile alloc] initWithGitDiffFile:*workdir] : nil); - return block((GTCheckoutNotifyFlags)why, nsPath, gtBaseline, gtTarget, gtWorkdir); -} - - (BOOL)moveHEADToReference:(GTReference *)reference error:(NSError **)error { NSParameterAssert(reference != nil); - - int gitError = git_repository_set_head(self.git_repository, reference.name.UTF8String, [self userSignatureForNow].git_signature, NULL); + + int gitError = git_repository_set_head(self.git_repository, reference.name.UTF8String); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to move HEAD to reference %@", reference.name]; } - + return gitError == GIT_OK; } - (BOOL)moveHEADToCommit:(GTCommit *)commit error:(NSError **)error { NSParameterAssert(commit != nil); - - int gitError = git_repository_set_head_detached(self.git_repository, commit.OID.git_oid, [self userSignatureForNow].git_signature, NULL); + + int gitError = git_repository_set_head_detached(self.git_repository, commit.OID.git_oid); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to move HEAD to commit %@", commit.SHA]; } - + return gitError == GIT_OK; } -- (BOOL)performCheckoutWithStrategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags error:(NSError **)error progressBlock:(GTCheckoutProgressBlock)progressBlock notifyBlock:(GTCheckoutNotifyBlock)notifyBlock { - - git_checkout_options checkoutOptions = GIT_CHECKOUT_OPTIONS_INIT; - - checkoutOptions.checkout_strategy = strategy; - checkoutOptions.progress_cb = checkoutProgressCallback; - checkoutOptions.progress_payload = (__bridge void *)progressBlock; - - checkoutOptions.notify_cb = checkoutNotifyCallback; - checkoutOptions.notify_flags = notifyFlags; - checkoutOptions.notify_payload = (__bridge void *)notifyBlock; - - int gitError = git_checkout_head(self.git_repository, &checkoutOptions); +- (BOOL)performCheckout:(GTObject *)target options:(GTCheckoutOptions * _Nullable)options error:(NSError **)error { + int gitError = git_checkout_tree(self.git_repository, target.git_object, options.git_checkoutOptions); if (gitError < GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to checkout tree."]; } - return gitError == GIT_OK; } -- (BOOL)checkoutCommit:(GTCommit *)targetCommit strategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags error:(NSError **)error progressBlock:(GTCheckoutProgressBlock)progressBlock notifyBlock:(GTCheckoutNotifyBlock)notifyBlock { - BOOL success = [self moveHEADToCommit:targetCommit error:error]; +- (BOOL)checkoutCommit:(GTCommit *)targetCommit options:(GTCheckoutOptions *)options error:(NSError **)error { + BOOL success = [self performCheckout:targetCommit options:options error:error]; if (success == NO) return NO; - - return [self performCheckoutWithStrategy:strategy notifyFlags:notifyFlags error:error progressBlock:progressBlock notifyBlock:notifyBlock]; + return [self moveHEADToCommit:targetCommit error:error]; } -- (BOOL)checkoutReference:(GTReference *)targetReference strategy:(GTCheckoutStrategyType)strategy notifyFlags:(GTCheckoutNotifyFlags)notifyFlags error:(NSError **)error progressBlock:(GTCheckoutProgressBlock)progressBlock notifyBlock:(GTCheckoutNotifyBlock)notifyBlock { - BOOL success = [self moveHEADToReference:targetReference error:error]; +- (BOOL)checkoutReference:(GTReference *)targetReference options:(GTCheckoutOptions *)options error:(NSError **)error { + GTOID *targetOID = [targetReference targetOID]; + GTObject *target = [self lookUpObjectByOID:targetOID error:error]; + if (target == nil) return NO; + BOOL success = [self performCheckout:target options:options error:error]; if (success == NO) return NO; - - return [self performCheckoutWithStrategy:strategy notifyFlags:notifyFlags error:error progressBlock:progressBlock notifyBlock:notifyBlock]; + return [self moveHEADToReference:targetReference error:error]; } -- (BOOL)checkoutCommit:(GTCommit *)target strategy:(GTCheckoutStrategyType)strategy error:(NSError **)error progressBlock:(GTCheckoutProgressBlock)progressBlock { - return [self checkoutCommit:target strategy:strategy notifyFlags:GTCheckoutNotifyNone error:error progressBlock:progressBlock notifyBlock:nil]; +- (BOOL)checkoutTree:(GTTree *)targetTree options:(GTCheckoutOptions * _Nullable)options error:(NSError **)error { + return [self performCheckout:targetTree options:options error:error]; } -- (BOOL)checkoutReference:(GTReference *)target strategy:(GTCheckoutStrategyType)strategy error:(NSError **)error progressBlock:(GTCheckoutProgressBlock)progressBlock { - return [self checkoutReference:target strategy:strategy notifyFlags:GTCheckoutNotifyNone error:error progressBlock:progressBlock notifyBlock:nil]; +- (BOOL)checkoutIndex:(GTIndex *)index options:(GTCheckoutOptions *)options error:(NSError **)error { + int gitError = git_checkout_index(self.git_repository, index.git_index, options.git_checkoutOptions); + if (gitError < GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to checkout index."]; + return NO; + } + return YES; } - (void)flushAttributesCache { @@ -794,7 +914,7 @@ - (GTFilterList *)filterListWithPath:(NSString *)path blob:(GTBlob *)blob mode:( NSParameterAssert(path != nil); git_filter_list *list = NULL; - int gitError = git_filter_list_load(&list, self.git_repository, blob.git_blob, path.fileSystemRepresentation, (git_filter_mode_t)mode, options); + int gitError = git_filter_list_load(&list, self.git_repository, blob.git_blob, path.UTF8String, (git_filter_mode_t)mode, options); if (gitError != GIT_OK) { if (success != NULL) *success = NO; if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to load filter list for %@", path]; @@ -810,4 +930,121 @@ - (GTFilterList *)filterListWithPath:(NSString *)path blob:(GTBlob *)blob mode:( } } +- (BOOL)calculateAhead:(size_t *)ahead behind:(size_t *)behind ofOID:(GTOID *)headOID relativeToOID:(GTOID *)baseOID error:(NSError **)error { + NSParameterAssert(headOID != nil); + NSParameterAssert(baseOID != nil); + + int errorCode = git_graph_ahead_behind(ahead, behind, self.git_repository, headOID.git_oid, baseOID.git_oid); + if (errorCode != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:errorCode description:@"Failed to calculate ahead/behind count of %@ relative to %@", headOID, baseOID]; + + return NO; + } + + return YES; +} + +- (GTEnumerator *)enumeratorForUniqueCommitsFromOID:(GTOID *)fromOID relativeToOID:(GTOID *)relativeOID error:(NSError **)error { + NSParameterAssert(fromOID != nil); + NSParameterAssert(relativeOID != nil); + + GTEnumerator *enumerator = [[GTEnumerator alloc] initWithRepository:self error:error]; + if (enumerator == nil) return nil; + + BOOL success = [enumerator pushSHA:fromOID.SHA error:error]; + if (!success) return nil; + + success = [enumerator hideSHA:relativeOID.SHA error:error]; + if (!success) return nil; + + return enumerator; +} + +- (BOOL)calculateState:(GTRepositoryStateType *)state withError:(NSError **)error { + NSParameterAssert(state != NULL); + + int result = git_repository_state(self.git_repository); + if (result < 0) { + if (error != NULL) *error = [NSError git_errorFor:result description:@"Failed to calculate repository state"]; + return NO; + } + + *state = result; + return YES; +} + +- (BOOL)cleanupStateWithError:(NSError **)error { + int errorCode = git_repository_state_cleanup(self.git_repository); + if (errorCode != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:errorCode description:@"Failed to clean up repository state"]; + } + return YES; +} + +#pragma mark Notes + +- (GTNote *)createNote:(NSString *)note target:(GTObject *)theTarget referenceName:(NSString *)referenceName author:(GTSignature *)author committer:(GTSignature *)committer overwriteIfExists:(BOOL)overwrite error:(NSError **)error { + git_oid oid; + + int gitError = git_note_create(&oid, self.git_repository, referenceName.UTF8String, author.git_signature, committer.git_signature, theTarget.OID.git_oid, [note UTF8String], overwrite ? 1 : 0); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create a note in repository"]; + + return nil; + } + + return [[GTNote alloc] initWithTargetOID:theTarget.OID repository:self referenceName:referenceName error:error]; +} + +- (BOOL)removeNoteFromObject:(GTObject *)parentObject referenceName:(NSString *)referenceName author:(GTSignature *)author committer:(GTSignature *)committer error:(NSError **)error { + int gitError = git_note_remove(self.git_repository, referenceName.UTF8String, author.git_signature, committer.git_signature, parentObject.OID.git_oid); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to delete note from %@", parentObject]; + return NO; + } + + return YES; +} + +- (BOOL)enumerateNotesWithReferenceName:(NSString *)referenceName error:(NSError **)error usingBlock:(void (^)(GTNote *note, GTObject *object, NSError *error, BOOL *stop))block { + git_note_iterator *iter = NULL; + + int gitError = git_note_iterator_new(&iter, self.git_repository, referenceName.UTF8String); + + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to enumerate notes"]; + return NO; + } + + @onExit { + git_note_iterator_free(iter); + }; + + git_oid note_id; + git_oid object_id; + BOOL success = YES; + int iterError = GIT_OK; + + while ((iterError = git_note_next(¬e_id, &object_id, iter)) == GIT_OK) { + NSError *lookupErr = nil; + + GTNote *note = [[GTNote alloc] initWithTargetOID:[GTOID oidWithGitOid:&object_id] repository:self referenceName:referenceName error:&lookupErr]; + GTObject *obj = nil; + + if (note != nil) obj = [self lookUpObjectByGitOid:&object_id error:&lookupErr]; + + BOOL stop = NO; + block(note, obj, lookupErr, &stop); + if (stop) { + break; + } + } + + if (iterError != GIT_OK && iterError != GIT_ITEROVER) { + if (error != NULL) *error = [NSError git_errorFor:iterError description:@"Iterator error"]; + } + + return success; +} + @end diff --git a/Classes/GTSignature.h b/ObjectiveGit/GTSignature.h similarity index 52% rename from Classes/GTSignature.h rename to ObjectiveGit/GTSignature.h index 5fd03dc73..566fe1e8f 100644 --- a/Classes/GTSignature.h +++ b/ObjectiveGit/GTSignature.h @@ -30,39 +30,43 @@ #import "GTObject.h" -// A git signature. +NS_ASSUME_NONNULL_BEGIN + +/// A git signature. @interface GTSignature : NSObject -// The name of the person. -@property (nonatomic, readonly, copy) NSString *name; +/// The name of the person. +@property (nonatomic, readonly, copy) NSString * _Nullable name; -// The email of the person. -@property (nonatomic, readonly, copy) NSString *email; +/// The email of the person. +@property (nonatomic, readonly, copy) NSString * _Nullable email; -// The time when the action happened. -@property (nonatomic, readonly, strong) NSDate *time; +/// The time when the action happened. +@property (nonatomic, readonly, strong) NSDate * _Nullable time; -// The time zone that `time` should be interpreted relative to. -@property (nonatomic, readonly, copy) NSTimeZone *timeZone; +/// The time zone that `time` should be interpreted relative to. +@property (nonatomic, readonly, copy) NSTimeZone * _Nullable timeZone; -// Initializes the receiver with the given signature. -// -// git_signature - The signature to wrap. This must not be NULL. -// -// Returns an initialized GTSignature, or nil if an error occurs. -- (id)initWithGitSignature:(const git_signature *)git_signature; +/// Initializes the receiver with the given signature. +/// +/// git_signature - The signature to wrap. This must not be NULL. +/// +/// Returns an initialized GTSignature, or nil if an error occurs. +- (instancetype _Nullable)initWithGitSignature:(const git_signature *)git_signature; -// Initializes the receiver with the given information. -// -// name - The name of the person. This must not be nil. -// email - The email of the person. This must not be nil. -// time - The time of the action, interpreted relative to the default time -// zone. This may be nil. -// -// Returns an initialized GTSignature, or nil if an error occurs. -- (id)initWithName:(NSString *)name email:(NSString *)email time:(NSDate *)time; +/// Initializes the receiver with the given information. +/// +/// name - The name of the person. This must not be nil. +/// email - The email of the person. This must not be nil. +/// time - The time of the action, interpreted relative to the default time +/// zone. This may be nil. +/// +/// Returns an initialized GTSignature, or nil if an error occurs. +- (instancetype _Nullable)initWithName:(NSString *)name email:(NSString *)email time:(NSDate * _Nullable)time; -// The underlying `git_signature` object. +/// The underlying `git_signature` object. - (const git_signature *)git_signature __attribute__((objc_returns_inner_pointer)); @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTSignature.m b/ObjectiveGit/GTSignature.m similarity index 91% rename from Classes/GTSignature.m rename to ObjectiveGit/GTSignature.m index 100a900f7..f79aeeadc 100644 --- a/Classes/GTSignature.m +++ b/ObjectiveGit/GTSignature.m @@ -30,6 +30,9 @@ #import "GTSignature.h" #import "NSDate+GTTimeAdditions.h" +#import "git2/errors.h" +#import "git2/signature.h" + @interface GTSignature () @property (nonatomic, assign, readonly) git_signature *git_signature; @end @@ -45,7 +48,7 @@ - (void)dealloc { } } -- (id)initWithGitSignature:(const git_signature *)git_signature { +- (instancetype)initWithGitSignature:(const git_signature *)git_signature { NSParameterAssert(git_signature != NULL); self = [super init]; @@ -57,7 +60,7 @@ - (id)initWithGitSignature:(const git_signature *)git_signature { return self; } -- (id)initWithName:(NSString *)name email:(NSString *)email time:(NSDate *)time { +- (instancetype)initWithName:(NSString *)name email:(NSString *)email time:(NSDate *)time { NSParameterAssert(name != nil); NSParameterAssert(email != nil); @@ -108,10 +111,14 @@ - (NSString *)email { } - (NSDate *)time { + if (self.git_signature == NULL) return nil; + return [NSDate gt_dateFromGitTime:self.git_signature->when]; } - (NSTimeZone *)timeZone { + if (self.git_signature == NULL) return nil; + return [NSTimeZone gt_timeZoneFromGitTime:self.git_signature->when]; } diff --git a/ObjectiveGit/GTStatusDelta.h b/ObjectiveGit/GTStatusDelta.h new file mode 100644 index 000000000..17299cba1 --- /dev/null +++ b/ObjectiveGit/GTStatusDelta.h @@ -0,0 +1,43 @@ +// +// GTStatusDelta.h +// ObjectiveGitFramework +// +// Created by Danny Greg on 08/08/2013. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "git2/diff.h" +#import + +@class GTDiffFile; + +NS_ASSUME_NONNULL_BEGIN + +/// Represents the status of a file in a repository. +@interface GTStatusDelta : NSObject + +/// The file as it was prior to the change represented by this status delta. +@property (nonatomic, readonly, copy) GTDiffFile * _Nullable oldFile; + +/// The file after the change represented by this status delta +@property (nonatomic, readonly, copy) GTDiffFile * _Nullable newFile __attribute__((ns_returns_not_retained)); + +/// The status of the file. +@property (nonatomic, readonly) GTDeltaType status; + +/// A float between 0 and 1 describing how similar the old and new +/// files are (where 0 is not at all and 1 is identical). +/// +/// Only useful when the status is `GTStatusDeltaStatusRenamed` or +/// `GTStatusDeltaStatusCopied`. +@property (nonatomic, readonly) double similarity; + +- (instancetype)init NS_UNAVAILABLE; + +/// Designated initializer. +- (instancetype _Nullable)initWithGitDiffDelta:(const git_diff_delta *)delta NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTStatusDelta.m b/ObjectiveGit/GTStatusDelta.m similarity index 81% rename from Classes/GTStatusDelta.m rename to ObjectiveGit/GTStatusDelta.m index 230691451..c1538675b 100644 --- a/Classes/GTStatusDelta.m +++ b/ObjectiveGit/GTStatusDelta.m @@ -12,13 +12,18 @@ @implementation GTStatusDelta +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + - (instancetype)initWithGitDiffDelta:(const git_diff_delta *)delta { self = [super init]; if (self == nil || delta == NULL) return nil; _oldFile = [[GTDiffFile alloc] initWithGitDiffFile:delta->old_file]; _newFile = [[GTDiffFile alloc] initWithGitDiffFile:delta->new_file]; - _status = (GTStatusDeltaStatus)delta->status; + _status = (GTDeltaType)delta->status; _similarity = (double)(delta->similarity / 100.0); return self; diff --git a/ObjectiveGit/GTSubmodule.h b/ObjectiveGit/GTSubmodule.h new file mode 100644 index 000000000..1e55fe16f --- /dev/null +++ b/ObjectiveGit/GTSubmodule.h @@ -0,0 +1,170 @@ +// +// GTSubmodule.h +// ObjectiveGitFramework +// +// Created by Justin Spahr-Summers on 2013-05-29. +// Copyright (c) 2013 GitHub, Inc. All rights reserved. +// + +#import +#import "GTObject.h" + +#import "git2/buffer.h" +#import "git2/submodule.h" + +@class GTOID; + +/// Determines which kinds of changes within the submodule repository will be +/// ignored when retrieving its status. +/// +/// These flags are mutually exclusive. +typedef NS_ENUM(NSInteger, GTSubmoduleIgnoreRule) { + GTSubmoduleIgnoreUnspecified = GIT_SUBMODULE_IGNORE_UNSPECIFIED, + GTSubmoduleIgnoreNone = GIT_SUBMODULE_IGNORE_NONE, + GTSubmoduleIgnoreUntracked = GIT_SUBMODULE_IGNORE_UNTRACKED, + GTSubmoduleIgnoreDirty = GIT_SUBMODULE_IGNORE_DIRTY, + GTSubmoduleIgnoreAll = GIT_SUBMODULE_IGNORE_ALL +}; + +/// Describes the status of a submodule. +/// +/// These flags may be ORed together. +typedef NS_OPTIONS(NSInteger, GTSubmoduleStatus) { + GTSubmoduleStatusUnknown = 0, + + GTSubmoduleStatusExistsInHEAD = GIT_SUBMODULE_STATUS_IN_HEAD, + GTSubmoduleStatusExistsInIndex = GIT_SUBMODULE_STATUS_IN_INDEX, + GTSubmoduleStatusExistsInConfig = GIT_SUBMODULE_STATUS_IN_CONFIG, + GTSubmoduleStatusExistsInWorkingDirectory = GIT_SUBMODULE_STATUS_IN_WD, + + GTSubmoduleStatusAddedToIndex = GIT_SUBMODULE_STATUS_INDEX_ADDED, + GTSubmoduleStatusDeletedFromIndex = GIT_SUBMODULE_STATUS_INDEX_DELETED, + GTSubmoduleStatusModifiedInIndex = GIT_SUBMODULE_STATUS_INDEX_MODIFIED, + + GTSubmoduleStatusUninitialized = GIT_SUBMODULE_STATUS_WD_UNINITIALIZED, + GTSubmoduleStatusAddedToWorkingDirectory = GIT_SUBMODULE_STATUS_WD_ADDED, + GTSubmoduleStatusDeletedFromWorkingDirectory = GIT_SUBMODULE_STATUS_WD_DELETED, + GTSubmoduleStatusModifiedInWorkingDirectory = GIT_SUBMODULE_STATUS_WD_MODIFIED, + + GTSubmoduleStatusDirtyIndex = GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED, + GTSubmoduleStatusDirtyWorkingDirectory = GIT_SUBMODULE_STATUS_WD_WD_MODIFIED, + GTSubmoduleStatusUntrackedFilesInWorkingDirectory = GIT_SUBMODULE_STATUS_WD_UNTRACKED +}; + +NS_ASSUME_NONNULL_BEGIN + +/// Represents a submodule within its parent repository. +@interface GTSubmodule : NSObject + +/// The repository that this submodule lives within. +@property (nonatomic, strong, readonly) GTRepository *parentRepository; + +/// The current ignore rule for this submodule. +@property (nonatomic, readonly, assign) GTSubmoduleIgnoreRule ignoreRule; + +/// The OID that the submodule is pinned to in the parent repository's index. +/// +/// If the submodule is not in the index, this will be nil. +@property (nonatomic, strong, readonly) GTOID * _Nullable indexOID; + +/// The OID that the submodule is pinned to in the parent repository's HEAD +/// commit. +/// +/// If the submodule is not in HEAD, this will be nil. +@property (nonatomic, strong, readonly) GTOID * _Nullable HEADOID; + +/// The OID that is checked out in the submodule repository. +/// +/// If the submodule is not checked out, this will be nil. +@property (nonatomic, strong, readonly) GTOID * _Nullable workingDirectoryOID; + +/// The name of this submodule. +@property (nonatomic, copy, readonly) NSString *name; + +/// The path to this submodule, relative to its parent repository's root. +@property (nonatomic, copy, readonly) NSString *path; + +/// The remote URL provided for this submodule, read from the parent repository's +/// `.git/config` or `.gitmodules` file. +@property (nonatomic, copy, readonly) NSString *URLString; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver to wrap the given submodule object. Designated initializer. +/// +/// submodule - The submodule to wrap. The receiver will not own this object, so +/// it must not be freed while the GTSubmodule is alive. This must +/// not be NULL. +/// repository - The repository that contains the submodule. This must not be +/// nil. +/// +/// Returns an initialized GTSubmodule, or nil if an error occurs. +- (instancetype _Nullable)initWithGitSubmodule:(git_submodule *)submodule parentRepository:(GTRepository *)repository NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_submodule` object. +- (git_submodule *)git_submodule __attribute__((objc_returns_inner_pointer)); + +/// Reloads the receiver's configuration from the parent repository. +/// +/// This will mutate properties on the receiver. +/// +/// Returns whether reloading succeeded. +- (BOOL)reload:(NSError **)error; + +/// Write a new ignore rule to disk and get the resulting submodule. The +/// receiver will not have the new ignore rule. To update the receiver, call +/// `-reload:`. +/// +/// ignoreRule - The ignore rule. +/// error - The error if one occurred. +/// +/// Returns the updated submodule or nil if an error occurred. +- (GTSubmodule * _Nullable)submoduleByUpdatingIgnoreRule:(GTSubmoduleIgnoreRule)ignoreRule error:(NSError **)error; + +/// Synchronizes the submodule repository's configuration files with the settings +/// from the parent repository. +/// +/// Returns whether the synchronization succeeded. +- (BOOL)sync:(NSError **)error; + +/// Opens the submodule repository. +/// +/// If the submodule is not currently checked out, this will fail. +/// +/// Returns the opened repository, or nil if an error occurs. +- (GTRepository * _Nullable)submoduleRepository:(NSError **)error; + +/// Calls `-statusWithIgnoreRule:error:` with the submodule's ignore rule. +- (GTSubmoduleStatus)status:(NSError **)error; + +/// Determine the status for the submodule using the given ignore rule. +/// +/// ignoreRule - The ignore rule to use in calculating status. +/// error - The error if one occurred. +/// +/// Returns the status or `GTSubmoduleStatusUnknown` if an error occurred. +- (GTSubmoduleStatus)statusWithIgnoreRule:(GTSubmoduleIgnoreRule)ignoreRule error:(NSError **)error; + +/// Initializes the submodule by copying its information into the parent +/// repository's `.git/config` file. This is equivalent to `git submodule init` +/// on the command line. +/// +/// overwrite - Whether to force an update to the `.git/config` file. If NO, +/// existing entries will not be overwritten. +/// error - If not NULL, set to any error that occurs. +/// +/// Returns whether the initialization succeeded. +- (BOOL)writeToParentConfigurationDestructively:(BOOL)overwrite error:(NSError **)error; + +/// Add the current HEAD to the parent repository's index. +/// +/// Note that it does *not* write the index. +/// +/// error - The error if one occurred. +/// +/// Returns whether the add was successful. +- (BOOL)addToIndex:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTSubmodule.m b/ObjectiveGit/GTSubmodule.m similarity index 73% rename from Classes/GTSubmodule.m rename to ObjectiveGit/GTSubmodule.m index 551181aee..5d61b20c2 100644 --- a/Classes/GTSubmodule.m +++ b/ObjectiveGit/GTSubmodule.m @@ -11,6 +11,8 @@ #import "GTRepository.h" #import "NSError+Git.h" +#import "git2/errors.h" + @interface GTSubmodule () @property (nonatomic, assign, readonly) git_submodule *git_submodule; @end @@ -23,8 +25,16 @@ - (GTSubmoduleIgnoreRule)ignoreRule { return (GTSubmoduleIgnoreRule)git_submodule_ignore(self.git_submodule); } -- (void)setIgnoreRule:(GTSubmoduleIgnoreRule)ignoreRule { - git_submodule_set_ignore(self.git_submodule, (git_submodule_ignore_t)ignoreRule); +- (GTSubmodule *)submoduleByUpdatingIgnoreRule:(GTSubmoduleIgnoreRule)ignoreRule error:(NSError **)error { + int result = git_submodule_set_ignore(self.parentRepository.git_repository, git_submodule_name(self.git_submodule), (git_submodule_ignore_t)ignoreRule); + if (result != GIT_OK) { + if (error != NULL) { + *error = [NSError git_errorFor:result description:@"Couldn't set submodule ignore rule."]; + } + return nil; + } + + return [self.parentRepository submoduleWithName:self.name error:error]; } - (GTOID *)indexOID { @@ -50,23 +60,32 @@ - (GTOID *)workingDirectoryOID { - (NSString *)name { const char *cName = git_submodule_name(self.git_submodule); - if (cName == NULL) return nil; + NSAssert(cName != NULL, @"Unexpected nil submodule name"); - return @(cName); + NSString *name = @(cName); + NSAssert(name, @"name is nil"); + + return name; } - (NSString *)path { const char *cPath = git_submodule_path(self.git_submodule); - if (cPath == NULL) return nil; + NSAssert(cPath != NULL, @"Unexpected nil submodule path"); + + NSString *path = @(cPath); + NSAssert(path, @"message is nil"); - return @(cPath); + return path; } - (NSString *)URLString { const char *cURL = git_submodule_url(self.git_submodule); - if (cURL == NULL) return nil; + NSAssert(cURL != NULL, @"Unexpected nil submodule URL"); + + NSString *URL = @(cURL); + NSAssert(URL, @"URL is nil"); - return @(cURL); + return URL; } #pragma mark Lifecycle @@ -77,7 +96,12 @@ - (void)dealloc { } } -- (id)initWithGitSubmodule:(git_submodule *)submodule parentRepository:(GTRepository *)repository { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithGitSubmodule:(git_submodule *)submodule parentRepository:(GTRepository *)repository { NSParameterAssert(submodule != NULL); NSParameterAssert(repository != nil); @@ -92,9 +116,9 @@ - (id)initWithGitSubmodule:(git_submodule *)submodule parentRepository:(GTReposi #pragma mark Inspection -- (GTSubmoduleStatus)status:(NSError **)error { +- (GTSubmoduleStatus)statusWithIgnoreRule:(GTSubmoduleIgnoreRule)ignoreRule error:(NSError **)error { unsigned status; - int gitError = git_submodule_status(&status, self.git_submodule); + int gitError = git_submodule_status(&status, self.parentRepository.git_repository, git_submodule_name(self.git_submodule), (git_submodule_ignore_t)ignoreRule); if (gitError != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get submodule %@ status.", self.name]; return GTSubmoduleStatusUnknown; @@ -103,6 +127,10 @@ - (GTSubmoduleStatus)status:(NSError **)error { return status; } +- (GTSubmoduleStatus)status:(NSError **)error { + return [self statusWithIgnoreRule:self.ignoreRule error:error]; +} + #pragma mark Manipulation - (BOOL)reload:(NSError **)error { diff --git a/Classes/GTTag.h b/ObjectiveGit/GTTag.h similarity index 67% rename from Classes/GTTag.h rename to ObjectiveGit/GTTag.h index 645327349..bd7318e64 100644 --- a/Classes/GTTag.h +++ b/ObjectiveGit/GTTag.h @@ -32,32 +32,39 @@ @class GTSignature; @class GTRepository; +NS_ASSUME_NONNULL_BEGIN + @interface GTTag : GTObject {} -// The author of the tag. -@property (nonatomic, readonly, strong) GTSignature *tagger; +/// The author of the tag. +@property (nonatomic, readonly, strong) GTSignature * _Nullable tagger; -// The description given when the tag was created. +/// The description given when the tag was created. @property (nonatomic, readonly, strong) NSString *message; -// The label of the tag. Generally a version number. +/// The label of the tag. Generally a version number. @property (nonatomic, readonly, strong) NSString *name; -// The 'tagged' object. -@property (nonatomic, readonly, strong) GTObject *target; +/// The 'tagged' object. +@property (nonatomic, readonly, strong) GTObject * _Nullable target; -// The type of the 'tagged' object. +/// The type of the 'tagged' object. @property (nonatomic, readonly) GTObjectType targetType; -// Recursively peel a tag until a non tag GTObject is found -// -// errro - Will be filled with a NSError object on failure. -// May be NULL. -// -// Returns the found object or nil on error. -- (id)objectByPeelingTagError:(NSError **)error; +/// Recursively peel a tag until a non tag GTObject is found +/// +/// error - Will be filled with a NSError object on failure. +/// May be NULL. +/// +/// Returns the found object or nil on error. +- (id _Nullable)objectByPeelingTagError:(NSError **)error; -// The underlying `git_object` as a `git_tag` object. +/// Delete the receiver. +- (BOOL)delete:(NSError **)error; + +/// The underlying `git_object` as a `git_tag` object. - (git_tag *)git_tag __attribute__((objc_returns_inner_pointer)); @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTTag.m b/ObjectiveGit/GTTag.m similarity index 82% rename from Classes/GTTag.m rename to ObjectiveGit/GTTag.m index e497c0085..e2cd31a94 100644 --- a/Classes/GTTag.m +++ b/ObjectiveGit/GTTag.m @@ -35,6 +35,9 @@ #import "NSString+Git.h" #import "GTOID.h" +#import "git2/errors.h" +#import "git2/tag.h" + @implementation GTTag - (NSString *)description { @@ -44,11 +47,15 @@ - (NSString *)description { #pragma mark API - (NSString *)message { - return @(git_tag_message(self.git_tag)); + NSString *message = @(git_tag_message(self.git_tag)); + NSAssert(message, @"message is nil"); + return message; } - (NSString *)name { - return @(git_tag_name(self.git_tag)); + NSString *name = @(git_tag_name(self.git_tag)); + NSAssert(name, @"message is nil"); + return name; } - (GTObject *)target { @@ -81,4 +88,13 @@ - (id)objectByPeelingTagError:(NSError **)error { return [[GTObject alloc] initWithObj:target inRepository:self.repository]; } +- (BOOL)delete:(NSError **)error { + int gitError = git_tag_delete(self.repository.git_repository, self.name.UTF8String); + if (gitError != GIT_OK) { + if (error) *error = [NSError git_errorFor:gitError description:@"Tag deletion failed"]; + return NO; + } + return YES; +} + @end diff --git a/ObjectiveGit/GTTree.h b/ObjectiveGit/GTTree.h new file mode 100644 index 000000000..80ceb507f --- /dev/null +++ b/ObjectiveGit/GTTree.h @@ -0,0 +1,105 @@ +// +// GTTree.h +// ObjectiveGitFramework +// +// Created by Timothy Clem on 2/22/11. +// +// The MIT License +// +// Copyright (c) 2011 Tim Clem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + + +#import "GTObject.h" +#import "git2/tree.h" + +@class GTTreeEntry; +@class GTIndex; + +typedef NS_ENUM(NSInteger, GTTreeEnumerationOptions) { + GTTreeEnumerationOptionPre = GIT_TREEWALK_PRE, // Walk the tree in pre-order (subdirectories come first) + GTTreeEnumerationOptionPost = GIT_TREEWALK_POST, // Walk the tree in post-order (subdirectories come last) +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface GTTree : GTObject + +/// The number of entries in the tree. +@property (nonatomic, readonly) NSUInteger entryCount; + +/// The contents of the tree, as an array of whose objects are of type `GTTreeEntry` +@property (nonatomic, strong, readonly) NSArray * _Nullable entries; + +/// The underlying `git_object` as a `git_tree` object. +- (git_tree *)git_tree __attribute__((objc_returns_inner_pointer)); + +/// Get an entry at the specified index +/// +/// index - index to retreive entry from +/// +/// returns a GTTreeEntry or nil if there is nothing at the index +- (GTTreeEntry * _Nullable)entryAtIndex:(NSUInteger)index; + +/// Get an entry by name +/// +/// name - the name of the entry +/// +/// returns a GTTreeEntry or nil if there is nothing with the specified name +- (GTTreeEntry * _Nullable)entryWithName:(NSString *)name; + +/// Get an entry by path +/// +/// path - the path of the entry relative to the repository root +/// +/// returns a GTTreeEntry or nil if there is nothing with the specified path +- (GTTreeEntry * _Nullable)entryWithPath:(NSString *)path error:(NSError **)error; + +/// Enumerates the contents of the tree +/// +/// options - One of `GTTreeEnumerationOptionPre` (for pre-order walks) or +/// `GTTreeEnumerationOptionPost` (for post-order walks). +/// error - The error if one occurred. +/// block - A block that will be invoked with the current entry, a +/// (repository relative) path to the root of the current entry, +/// and a stop parameter to abort the walk. Cannot be nil. +/// Return `YES` to move into the descendants of the entry. +/// Return `NO` to skip the entry's descendants. +/// Returning `YES` or `NO` only matters when in pre-order mode. +/// +/// Returns `YES` if the enumeration completed successfully, `NO` otherwise. +- (BOOL)enumerateEntriesWithOptions:(GTTreeEnumerationOptions)options error:(NSError **)error block:(BOOL (^)(GTTreeEntry *entry, NSString *root, BOOL *stop))block; + +/// Merges the given tree into the receiver in memory and produces the result as +/// an index. +/// +/// otherTree - The tree with which the receiver should be merged. Cannot be +/// nil. +/// ancestorTree - The common ancestor of the two trees, or nil if none. +/// error - The error if one occurred. +/// +/// Returns an index which represents the result of the merge, or nil if an error +/// occurred. +- (GTIndex * _Nullable)merge:(GTTree *)otherTree ancestor:(GTTree * _Nullable)ancestorTree error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTTree.m b/ObjectiveGit/GTTree.m similarity index 87% rename from Classes/GTTree.m rename to ObjectiveGit/GTTree.m index c46de23cc..97b7aa6c3 100644 --- a/Classes/GTTree.m +++ b/ObjectiveGit/GTTree.m @@ -33,6 +33,9 @@ #import "GTIndex.h" #import "NSError+Git.h" +#import "git2/errors.h" +#import "git2/merge.h" + typedef BOOL (^GTTreeEnumerationBlock)(GTTreeEntry *entry, NSString *root, BOOL *stop); typedef struct GTTreeEnumerationStruct { @@ -53,16 +56,29 @@ - (NSUInteger)entryCount { return (NSUInteger)git_tree_entrycount(self.git_tree); } -- (GTTreeEntry *)createEntryWithEntry:(const git_tree_entry *)entry { - return (entry != NULL ? [GTTreeEntry entryWithEntry:entry parentTree:self] : nil); +- (GTTreeEntry *)createEntryWithEntry:(const git_tree_entry *)entry error:(NSError **)error { + return (entry != NULL ? [GTTreeEntry entryWithEntry:entry parentTree:self error:nil] : nil); } - (GTTreeEntry *)entryAtIndex:(NSUInteger)index { - return [self createEntryWithEntry:git_tree_entry_byindex(self.git_tree, index)]; + return [self createEntryWithEntry:git_tree_entry_byindex(self.git_tree, index) error:nil]; } - (GTTreeEntry *)entryWithName:(NSString *)name { - return [self createEntryWithEntry:git_tree_entry_byname(self.git_tree, name.UTF8String)]; + return [self createEntryWithEntry:git_tree_entry_byname(self.git_tree, name.UTF8String) error:nil]; +} + +- (GTTreeEntry *)entryWithPath:(NSString *)path error:(NSError **)error { + git_tree_entry *internalEntry = NULL; + int gitError = git_tree_entry_bypath(&internalEntry, self.git_tree, path.UTF8String); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to get tree entry %@", path]; + return nil; + } + + GTTreeEntry *entry = [self createEntryWithEntry:internalEntry error:error]; + git_tree_entry_free(internalEntry); + return entry; } - (git_tree *)git_tree { @@ -76,7 +92,7 @@ static int treewalk_cb(const char *root, const git_tree_entry *git_entry, void * NSString *rootString = @(root); GTTreeEntry *parentEntry = enumerationStruct->directoryStructure[rootString]; GTTree *parentTree = parentEntry != nil ? parentEntry.tree : enumerationStruct->myself; - GTTreeEntry *entry = [GTTreeEntry entryWithEntry:git_entry parentTree:parentTree]; + GTTreeEntry *entry = [GTTreeEntry entryWithEntry:git_entry parentTree:parentTree error:nil]; if (entry.type == GTObjectTypeTree) { NSString *path = [rootString stringByAppendingPathComponent:entry.name]; diff --git a/ObjectiveGit/GTTreeBuilder.h b/ObjectiveGit/GTTreeBuilder.h new file mode 100644 index 000000000..cfa5f8842 --- /dev/null +++ b/ObjectiveGit/GTTreeBuilder.h @@ -0,0 +1,133 @@ +// +// GTTreeBuilder.h +// ObjectiveGitFramework +// +// Created by Johnnie Walker on 17/05/2013. +// +// The MIT License +// +// Copyright (c) 2013 Johnnie Walker +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "git2/types.h" + +/// The mode of an index or tree entry. +typedef NS_ENUM(NSInteger, GTFileMode) { + GTFileModeUnreadable = GIT_FILEMODE_UNREADABLE, + GTFileModeTree = GIT_FILEMODE_TREE, + GTFileModeBlob = GIT_FILEMODE_BLOB, + GTFileModeBlobExecutable = GIT_FILEMODE_BLOB_EXECUTABLE, + GTFileModeLink = GIT_FILEMODE_LINK, + GTFileModeCommit = GIT_FILEMODE_COMMIT +}; + +@class GTTree; +@class GTTreeEntry; +@class GTRepository; +@class GTOID; + +NS_ASSUME_NONNULL_BEGIN + +/// A tree builder is used to create or modify trees in memory and write them as +/// tree objects to a repository. +@interface GTTreeBuilder : NSObject + +/// Get the number of entries listed in a treebuilder. +@property (nonatomic, readonly) NSUInteger entryCount; + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializes the receiver, optionally from an existing tree. Designated initializer. +/// +/// treeOrNil - Source tree (or nil) +/// repository - The repository in which to build the tree. Must not be nil. +/// error - The error if one occurred. +/// +/// Returns the initialized object, or nil if an error occurred. +- (instancetype _Nullable)initWithTree:(GTTree * _Nullable)treeOrNil repository:(GTRepository *)repository error:(NSError **)error NS_DESIGNATED_INITIALIZER; + +/// The underlying `git_treebuilder` object. +- (git_treebuilder *)git_treebuilder __attribute__((objc_returns_inner_pointer)); + +/// Clear all the entires in the builder. +- (void)clear; + +/// Filter the entries in the tree. +/// +/// filterBlock - A block which returns YES for entries which should be filtered +/// from the index. Must not be nil. +- (void)filter:(BOOL (^)(const git_tree_entry *entry))filterBlock; + +/// Get an entry from the builder from its file name. +/// +/// fileName - File name for the object in the index. Cannot be nil. +/// +/// Returns the matching entry or nil if it doesn't exist. +- (GTTreeEntry * _Nullable)entryWithFileName:(NSString *)fileName; + +/// Adds or updates the entry for the file name with the given data. When the +/// tree is written, a blob will be inserted into the object database containing +/// `data`. +/// +/// data - The data for the entry. Cannot be nil. +/// fileName - The file name for the object. Cannot be nil. +/// fileMode - The file mode for the object. +/// error - The error if one occurred. +/// +/// Returns the added entry, or nil if an error occurred +- (GTTreeEntry * _Nullable)addEntryWithData:(NSData *)data fileName:(NSString *)fileName fileMode:(GTFileMode)fileMode error:(NSError **)error; + +/// Add or update an entry to the builder. +/// +/// oid - The OID of a git object aleady stored in the repository. +/// fileName - File name for the object in the index. Cannot be nil. +/// fileMode - File mode for the object in the index. +/// error - The error if one occurred. +/// +/// If an entry named `filename` already exists, its attributes will be updated +/// with the given ones. +/// +/// No attempt is made to ensure that the provided oid points to an existing git +/// object in the object database, nor that the attributes make sense regarding +/// the type of the pointed at object. +/// +/// Returns the added entry, or nil if an error occurred. +- (GTTreeEntry * _Nullable)addEntryWithOID:(GTOID *)oid fileName:(NSString *)fileName fileMode:(GTFileMode)fileMode error:(NSError **)error; + +/// Remove an entry from the builder by its file name. +/// +/// fileName - File name for the object in the tree. Must not be nil. +/// error - The error if one occurred. +/// +/// Returns YES if the entry was removed, or NO if an error occurred. +- (BOOL)removeEntryWithFileName:(NSString *)fileName error:(NSError **)error; + +/// Write the contents of the tree builder as a tree object. +/// +/// error - The error if one occurred. +/// +/// Returns the written tree, or nil if an error occurred. +- (GTTree * _Nullable)writeTree:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTTreeBuilder.m b/ObjectiveGit/GTTreeBuilder.m similarity index 69% rename from Classes/GTTreeBuilder.m rename to ObjectiveGit/GTTreeBuilder.m index b2200d754..d53118e9d 100644 --- a/Classes/GTTreeBuilder.m +++ b/ObjectiveGit/GTTreeBuilder.m @@ -36,16 +36,12 @@ #import "NSError+Git.h" #import "GTOID.h" +#import "git2/errors.h" + @interface GTTreeBuilder () @property (nonatomic, assign, readonly) git_treebuilder *git_treebuilder; - -// Data to be written with the tree, keyed by the file name. This should only be -// accessed while synchronized on self. -// -// This is needed because we don't want to add the entries to the object -// database until the tree's been written. -@property (nonatomic, strong, readonly) NSMutableDictionary *fileNameToPendingData; +@property (nonatomic, strong, readonly) GTRepository *repository; @end @@ -59,18 +55,24 @@ - (NSUInteger)entryCount { #pragma mark Lifecycle -- (id)initWithTree:(GTTree *)treeOrNil error:(NSError **)error { +- (instancetype)init { + NSAssert(NO, @"Call to an unavailable initializer."); + return nil; +} + +- (instancetype)initWithTree:(GTTree *)treeOrNil repository:(GTRepository *)repository error:(NSError **)error { + NSParameterAssert(repository != nil); + self = [super init]; if (self == nil) return nil; - int status = git_treebuilder_create(&_git_treebuilder, treeOrNil.git_tree); + int status = git_treebuilder_new(&_git_treebuilder, repository.git_repository, treeOrNil.git_tree); if (status != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to create tree builder with tree %@.", treeOrNil.SHA]; return nil; } - _fileNameToPendingData = [NSMutableDictionary dictionary]; - + _repository = repository; return self; } @@ -84,7 +86,7 @@ - (void)dealloc { #pragma mark Modification - (void)clear { - git_treebuilder_clear(self.git_treebuilder); + git_treebuilder_clear(self.git_treebuilder); } static int filter_callback(const git_tree_entry *entry, void *payload) { @@ -103,20 +105,19 @@ - (GTTreeEntry *)entryWithFileName:(NSString *)fileName { const git_tree_entry *entry = git_treebuilder_get(self.git_treebuilder, fileName.UTF8String); if (entry == NULL) return nil; - - return [GTTreeEntry entryWithEntry:entry parentTree:nil]; + + return [GTTreeEntry entryWithEntry:entry parentTree:nil error:nil]; } - (GTTreeEntry *)addEntryWithData:(NSData *)data fileName:(NSString *)fileName fileMode:(GTFileMode)fileMode error:(NSError **)error { NSParameterAssert(data != nil); NSParameterAssert(fileName != nil); - GTOID *OID = [GTOID OIDByHashingData:data type:GTObjectTypeBlob error:error]; - if (OID == nil) return nil; + GTObjectDatabase *odb = [self.repository objectDatabaseWithError:error]; + if (odb == nil) return nil; - @synchronized (self) { - self.fileNameToPendingData[fileName] = data; - } + GTOID *OID = [odb writeData:data type:GTObjectTypeBlob error:error]; + if (OID == nil) return nil; return [self addEntryWithOID:OID fileName:fileName fileMode:fileMode error:error]; } @@ -127,13 +128,13 @@ - (GTTreeEntry *)addEntryWithOID:(GTOID *)oid fileName:(NSString *)fileName file const git_tree_entry *entry = NULL; int status = git_treebuilder_insert(&entry, self.git_treebuilder, fileName.UTF8String, oid.git_oid, (git_filemode_t)fileMode); - + if (status != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to add entry %@ to tree builder.", oid.SHA]; return nil; } - - return [GTTreeEntry entryWithEntry:entry parentTree:nil]; + + return [GTTreeEntry entryWithEntry:entry parentTree:nil error:error]; } - (BOOL)removeEntryWithFileName:(NSString *)fileName error:(NSError **)error { @@ -142,53 +143,25 @@ - (BOOL)removeEntryWithFileName:(NSString *)fileName error:(NSError **)error { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to remove entry with name %@ from tree builder.", fileName]; } - @synchronized (self) { - [self.fileNameToPendingData removeObjectForKey:fileName]; - } - return status == GIT_OK; } -- (BOOL)writePendingDataToRepository:(GTRepository *)repository error:(NSError **)error { - NSDictionary *copied; - @synchronized (self) { - copied = [self.fileNameToPendingData copy]; - [self.fileNameToPendingData removeAllObjects]; - } - - if (copied.count != 0) { - GTObjectDatabase *odb = [repository objectDatabaseWithError:error]; - if (odb == nil) return NO; - - for (NSString *fileName in copied) { - NSData *data = copied[fileName]; - GTOID *dataOID = [odb writeData:data type:GTObjectTypeBlob error:error]; - if (dataOID == nil) return NO; - } - } - - return YES; -} - -- (GTTree *)writeTreeToRepository:(GTRepository *)repository error:(NSError **)error { - BOOL success = [self writePendingDataToRepository:repository error:error]; - if (!success) return nil; - +- (GTTree *)writeTree:(NSError **)error { git_oid treeOid; - int status = git_treebuilder_write(&treeOid, repository.git_repository, self.git_treebuilder); + int status = git_treebuilder_write(&treeOid, self.git_treebuilder); if (status != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to write tree in repository."]; return nil; } - + git_object *object = NULL; - status = git_object_lookup(&object, repository.git_repository, &treeOid, GIT_OBJ_TREE); + status = git_object_lookup(&object, self.repository.git_repository, &treeOid, GIT_OBJECT_TREE); if (status != GIT_OK) { if (error != NULL) *error = [NSError git_errorFor:status description:@"Failed to lookup tree in repository."]; return nil; } - - return [GTObject objectWithObj:object inRepository:repository]; + + return [GTObject objectWithObj:object inRepository:self.repository]; } @end diff --git a/Classes/GTTreeEntry.h b/ObjectiveGit/GTTreeEntry.h similarity index 52% rename from Classes/GTTreeEntry.h rename to ObjectiveGit/GTTreeEntry.h index 3e28273fc..f90bc135a 100644 --- a/Classes/GTTreeEntry.h +++ b/ObjectiveGit/GTTreeEntry.h @@ -31,46 +31,52 @@ @class GTTree; +NS_ASSUME_NONNULL_BEGIN + @interface GTTreeEntry : NSObject -// Initializer and convience methods. -- (instancetype)initWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree *)parent; -+ (instancetype)entryWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree *)parent; +/// Initializes the receiver. +- (instancetype _Nullable)initWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree * _Nullable)parent error:(NSError **)error; + +/// Convience class initializer. ++ (instancetype _Nullable)entryWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree * _Nullable)parent error:(NSError **)error; -// The underlying `git_tree_entry`. -- (const git_tree_entry *)git_tree_entry __attribute__((objc_returns_inner_pointer)); +/// The underlying `git_tree_entry`. +- (git_tree_entry *)git_tree_entry __attribute__((objc_returns_inner_pointer)); -// The entry's parent tree. This may be nil if nil is passed in to -initWithEntry: -@property (nonatomic, strong, readonly) GTTree *tree; +/// The entry's parent tree. This may be nil if nil parentTree is passed in to -initWithEntry: +@property (nonatomic, strong, readonly) GTTree * _Nullable tree; -// The filename of the entry +/// The filename of the entry @property (nonatomic, copy, readonly) NSString *name; -// The UNIX file attributes of the entry. +/// The UNIX file attributes of the entry. @property (nonatomic, readonly) NSInteger attributes; -// The SHA hash of the entry -@property (nonatomic, copy, readonly) NSString *SHA; +/// The SHA hash of the entry +@property (nonatomic, copy, readonly) NSString * _Nullable SHA; -// The type of GTObject that -object: will return. +/// The type of GTObject that -object: will return. @property (nonatomic, readonly) GTObjectType type; -// The OID of the entry. -@property (nonatomic, strong, readonly) GTOID *OID; +/// The OID of the entry. +@property (nonatomic, strong, readonly) GTOID * _Nullable OID; -// Convert the entry into an GTObject -// -// error - will be filled if an error occurs -// -// Returns this entry as a GTObject or nil if an error occurred. -- (GTObject *)GTObject:(NSError **)error; +/// Convert the entry into an GTObject +/// +/// error - will be filled if an error occurs +/// +/// Returns this entry as a GTObject or nil if an error occurred. +- (GTObject * _Nullable)GTObject:(NSError **)error; @end @interface GTObject (GTTreeEntry) -+ (instancetype)objectWithTreeEntry:(GTTreeEntry *)treeEntry error:(NSError **)error; -- (instancetype)initWithTreeEntry:(GTTreeEntry *)treeEntry error:(NSError **)error; ++ (instancetype _Nullable)objectWithTreeEntry:(GTTreeEntry *)treeEntry error:(NSError **)error; +- (instancetype _Nullable)initWithTreeEntry:(GTTreeEntry *)treeEntry error:(NSError **)error; @end + +NS_ASSUME_NONNULL_END diff --git a/Classes/GTTreeEntry.m b/ObjectiveGit/GTTreeEntry.m similarity index 81% rename from Classes/GTTreeEntry.m rename to ObjectiveGit/GTTreeEntry.m index 3873cc280..946b1f06f 100644 --- a/Classes/GTTreeEntry.m +++ b/ObjectiveGit/GTTreeEntry.m @@ -35,8 +35,10 @@ #import "NSString+Git.h" #import "GTOID.h" +#import "git2/errors.h" + @interface GTTreeEntry () -@property (nonatomic, assign, readonly) const git_tree_entry *git_tree_entry; +@property (nonatomic, assign, readonly) git_tree_entry *git_tree_entry; @end @implementation GTTreeEntry @@ -62,23 +64,39 @@ - (BOOL)isEqualToEntry:(GTTreeEntry *)treeEntry { return git_tree_entry_cmp(self.git_tree_entry, treeEntry.git_tree_entry) == 0 ? YES : NO; } +- (void)dealloc { + git_tree_entry_free(_git_tree_entry); +} + #pragma mark API -- (instancetype)initWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree *)parent { +- (instancetype)initWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree *)parent error:(NSError **)error { NSParameterAssert(theEntry != NULL); - if((self = [super init])) { - _git_tree_entry = theEntry; - _tree = parent; + + self = [super init]; + if (self == nil) return nil; + + git_tree_entry *copyOfEntry = nil; + int gitError = git_tree_entry_dup(©OfEntry, theEntry); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to duplicate tree entry."]; + return nil; } + + _git_tree_entry = copyOfEntry; + _tree = parent; + return self; } -+ (instancetype)entryWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree *)parent { - return [[self alloc] initWithEntry:theEntry parentTree:parent]; ++ (instancetype)entryWithEntry:(const git_tree_entry *)theEntry parentTree:(GTTree *)parent error:(NSError **)error { + return [[self alloc] initWithEntry:theEntry parentTree:parent error:error]; } - (NSString *)name { - return @(git_tree_entry_name(self.git_tree_entry)); + NSString *name = @(git_tree_entry_name(self.git_tree_entry)); + NSAssert(name, @"name was nil"); + return name; } - (NSInteger)attributes { diff --git a/Classes/ObjectiveGit.h b/ObjectiveGit/ObjectiveGit.h similarity index 81% rename from Classes/ObjectiveGit.h rename to ObjectiveGit/ObjectiveGit.h index c5ffec585..5c8cba5bc 100644 --- a/Classes/ObjectiveGit.h +++ b/ObjectiveGit/ObjectiveGit.h @@ -23,13 +23,25 @@ // THE SOFTWARE. // +#import #import "git2.h" +//! Project version number for ObjectiveGit. +FOUNDATION_EXPORT double ObjectiveGitVersionNumber; + +//! Project version string for ObjectiveGit. +FOUNDATION_EXPORT const unsigned char ObjectiveGitVersionString[]; + #import +#import #import #import #import +#import +#import #import +#import +#import #import #import #import @@ -57,12 +69,16 @@ #import #import #import +#import +#import +#import #import #import #import #import +#import #import #import diff --git a/Classes/ObjectiveGit.m b/ObjectiveGit/ObjectiveGit.m similarity index 71% rename from Classes/ObjectiveGit.m rename to ObjectiveGit/ObjectiveGit.m index fcc314fc2..b44bca615 100644 --- a/Classes/ObjectiveGit.m +++ b/ObjectiveGit/ObjectiveGit.m @@ -6,9 +6,9 @@ // Copyright (c) 2012 GitHub, Inc. All rights reserved. // -#import "git2.h" +#import "git2/global.h" __attribute__((constructor)) -static void GTSetupThreads(void) { - git_threads_init(); +static void GTSetup(void) { + git_libgit2_init(); } diff --git a/ObjectiveGitFramework.xcodeproj/project.pbxproj b/ObjectiveGitFramework.xcodeproj/project.pbxproj index eba4a63a6..26da37c95 100644 --- a/ObjectiveGitFramework.xcodeproj/project.pbxproj +++ b/ObjectiveGitFramework.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXAggregateTarget section */ @@ -11,7 +11,7 @@ isa = PBXAggregateTarget; buildConfigurationList = 6A28265417C69CB400C6A948 /* Build configuration list for PBXAggregateTarget "OpenSSL-iOS" */; buildPhases = ( - 6A28265317C69CB400C6A948 /* ShellScript */, + 6A28265317C69CB400C6A948 /* OpenSSL-iOS */, ); dependencies = ( ); @@ -22,7 +22,7 @@ isa = PBXAggregateTarget; buildConfigurationList = 6A3C609217D5963700382DFF /* Build configuration list for PBXAggregateTarget "libssh2-iOS" */; buildPhases = ( - 6A3C609117D5963700382DFF /* ShellScript */, + 6A3C609117D5963700382DFF /* libssh2-iOS */, ); dependencies = ( 6A3C609D17D5964E00382DFF /* PBXTargetDependency */, @@ -34,7 +34,7 @@ isa = PBXAggregateTarget; buildConfigurationList = D0A330EE16027F1E00A616FA /* Build configuration list for PBXAggregateTarget "libgit2" */; buildPhases = ( - D0A330F116027F2300A616FA /* ShellScript */, + D0A330F116027F2300A616FA /* libgit2 */, ); dependencies = ( ); @@ -45,10 +45,9 @@ isa = PBXAggregateTarget; buildConfigurationList = D0A330F316027F3700A616FA /* Build configuration list for PBXAggregateTarget "libgit2-iOS" */; buildPhases = ( - D0A330F616027F3B00A616FA /* ShellScript */, + D0A330F616027F3B00A616FA /* libgit2-iOS */, ); dependencies = ( - 6A28265B17C69D6300C6A948 /* PBXTargetDependency */, 6A3C609F17D5965700382DFF /* PBXTargetDependency */, ); name = "libgit2-iOS"; @@ -57,187 +56,117 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 04DB464E133AB5E200D9C624 /* NSError+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = BDE4C060130EFE2C00851650 /* NSError+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB464F133AB5E200D9C624 /* GTRepository.h in Headers */ = {isa = PBXBuildFile; fileRef = BDE4C062130EFE2C00851650 /* GTRepository.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4652133AB5E200D9C624 /* GTEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD8AE6D13131B8800CB5D40 /* GTEnumerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4653133AB5E200D9C624 /* GTTree.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6B040F131496B8001909D0 /* GTTree.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4654133AB5E200D9C624 /* GTTreeEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6B0415131496CC001909D0 /* GTTreeEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4655133AB5E200D9C624 /* GTBlob.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD627971318391200DE34D1 /* GTBlob.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4656133AB5E200D9C624 /* GTTag.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD62922131C03D600DE34D1 /* GTTag.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4657133AB5E200D9C624 /* GTIndex.h in Headers */ = {isa = PBXBuildFile; fileRef = BDFAF9C1131C1845000508BC /* GTIndex.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4658133AB5E200D9C624 /* GTIndexEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = BDFAF9C7131C1868000508BC /* GTIndexEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4659133AB5E200D9C624 /* GTReference.h in Headers */ = {isa = PBXBuildFile; fileRef = BD441E06131ED0C300187010 /* GTReference.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB465A133AB5E200D9C624 /* ObjectiveGit.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F6D9D81320451F00CC0BA8 /* ObjectiveGit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB465B133AB5E200D9C624 /* GTCommit.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C22A41314609A00992935 /* GTCommit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB465C133AB5E200D9C624 /* GTObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C22A71314625800992935 /* GTObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB465D133AB5E200D9C624 /* GTSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C254313148DC900992935 /* GTSignature.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB465E133AB5E200D9C624 /* GTBranch.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F50F56132054D800584FBE /* GTBranch.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 04DB4660133AB5EB00D9C624 /* NSError+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = BDE4C061130EFE2C00851650 /* NSError+Git.m */; }; - 04DB4661133AB5EB00D9C624 /* GTRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = BDE4C063130EFE2C00851650 /* GTRepository.m */; }; - 04DB4664133AB5EB00D9C624 /* GTEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD8AE6E13131B8800CB5D40 /* GTEnumerator.m */; }; - 04DB4665133AB5EB00D9C624 /* GTCommit.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C22A51314609A00992935 /* GTCommit.m */; }; - 04DB4666133AB5EB00D9C624 /* GTObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C22A81314625800992935 /* GTObject.m */; }; - 04DB4667133AB5EB00D9C624 /* GTSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C254413148DC900992935 /* GTSignature.m */; }; - 04DB4668133AB5EB00D9C624 /* GTTree.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6B0410131496B8001909D0 /* GTTree.m */; }; - 04DB4669133AB5EB00D9C624 /* GTTreeEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6B0416131496CC001909D0 /* GTTreeEntry.m */; }; - 04DB466A133AB5EB00D9C624 /* GTBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD627981318391200DE34D1 /* GTBlob.m */; }; - 04DB466B133AB5EB00D9C624 /* GTTag.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD62923131C03D600DE34D1 /* GTTag.m */; }; - 04DB466C133AB5EB00D9C624 /* GTIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = BDFAF9C2131C1845000508BC /* GTIndex.m */; }; - 04DB466D133AB5EB00D9C624 /* GTIndexEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = BDFAF9C8131C1868000508BC /* GTIndexEntry.m */; }; - 04DB466E133AB5EB00D9C624 /* GTReference.m in Sources */ = {isa = PBXBuildFile; fileRef = BD441E07131ED0C300187010 /* GTReference.m */; }; - 04DB466F133AB5EB00D9C624 /* GTBranch.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F50F57132054D800584FBE /* GTBranch.m */; }; - 188DC01917FC1571007350CD /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 188DC01817FC1571007350CD /* libz.dylib */; }; 200578C518932A82001C06C3 /* GTBlameSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 200578C418932A82001C06C3 /* GTBlameSpec.m */; }; - 20702DAF18A1F38A009FB457 /* GTBlame.h in Headers */ = {isa = PBXBuildFile; fileRef = DD3D9510182A81E1004AF532 /* GTBlame.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 20702DB118A1F38A009FB457 /* GTBlameHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = DD3D951A182AB25C004AF532 /* GTBlameHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2089E43C17D9A58000F451DA /* GTTagSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 2089E43B17D9A58000F451DA /* GTTagSpec.m */; }; 20F43DE318A2F668007D3621 /* GTRepository+Blame.h in Headers */ = {isa = PBXBuildFile; fileRef = 20F43DE118A2F667007D3621 /* GTRepository+Blame.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 20F43DE418A2F668007D3621 /* GTRepository+Blame.h in Headers */ = {isa = PBXBuildFile; fileRef = 20F43DE118A2F667007D3621 /* GTRepository+Blame.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20F43DE618A2F668007D3621 /* GTRepository+Blame.m in Sources */ = {isa = PBXBuildFile; fileRef = 20F43DE218A2F667007D3621 /* GTRepository+Blame.m */; }; - 20F43DE718A2F668007D3621 /* GTRepository+Blame.m in Sources */ = {isa = PBXBuildFile; fileRef = 20F43DE218A2F667007D3621 /* GTRepository+Blame.m */; }; + 23BB67C11C7DF60300A37A66 /* GTRepository+PullSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F8EFA0361B405020000FF7D0 /* GTRepository+PullSpec.m */; }; + 23BB67C21C7DF60400A37A66 /* GTRepository+PullSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F8EFA0361B405020000FF7D0 /* GTRepository+PullSpec.m */; }; + 23F39FAD1C86DB1C00849F3C /* GTRepository+Merging.h in Headers */ = {isa = PBXBuildFile; fileRef = 23F39FAB1C86DB1C00849F3C /* GTRepository+Merging.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 23F39FAE1C86DB1C00849F3C /* GTRepository+Merging.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F39FAC1C86DB1C00849F3C /* GTRepository+Merging.m */; }; + 23F39FAF1C86DD0A00849F3C /* GTRepository+Merging.h in Headers */ = {isa = PBXBuildFile; fileRef = 23F39FAB1C86DB1C00849F3C /* GTRepository+Merging.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 23F39FB01C86E01800849F3C /* GTRepository+Merging.m in Sources */ = {isa = PBXBuildFile; fileRef = 23F39FAC1C86DB1C00849F3C /* GTRepository+Merging.m */; }; 3011D86B1668E48500CE3409 /* GTDiffFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8691668E48500CE3409 /* GTDiffFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3011D86C1668E48500CE3409 /* GTDiffFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8691668E48500CE3409 /* GTDiffFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3011D86D1668E48500CE3409 /* GTDiffFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D86A1668E48500CE3409 /* GTDiffFile.m */; }; - 3011D86E1668E48500CE3409 /* GTDiffFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D86A1668E48500CE3409 /* GTDiffFile.m */; }; 3011D8711668E78500CE3409 /* GTDiffHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D86F1668E78500CE3409 /* GTDiffHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3011D8721668E78500CE3409 /* GTDiffHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D86F1668E78500CE3409 /* GTDiffHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3011D8731668E78500CE3409 /* GTDiffHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8701668E78500CE3409 /* GTDiffHunk.m */; }; - 3011D8741668E78500CE3409 /* GTDiffHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8701668E78500CE3409 /* GTDiffHunk.m */; }; 3011D8771668F29600CE3409 /* GTDiffDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8751668F29600CE3409 /* GTDiffDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3011D8781668F29600CE3409 /* GTDiffDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8751668F29600CE3409 /* GTDiffDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3011D8791668F29600CE3409 /* GTDiffDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8761668F29600CE3409 /* GTDiffDelta.m */; }; - 3011D87A1668F29600CE3409 /* GTDiffDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8761668F29600CE3409 /* GTDiffDelta.m */; }; - 306123AC17EA5261006591D4 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 306123A917EA5261006591D4 /* EXTScope.h */; }; - 306123AD17EA5261006591D4 /* EXTScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 306123A917EA5261006591D4 /* EXTScope.h */; }; 306123AE17EA5261006591D4 /* EXTScope.m in Sources */ = {isa = PBXBuildFile; fileRef = 306123AA17EA5261006591D4 /* EXTScope.m */; }; - 306123AF17EA5261006591D4 /* EXTScope.m in Sources */ = {isa = PBXBuildFile; fileRef = 306123AA17EA5261006591D4 /* EXTScope.m */; }; - 306123B017EA5261006591D4 /* EXTScope.m in Sources */ = {isa = PBXBuildFile; fileRef = 306123AA17EA5261006591D4 /* EXTScope.m */; }; - 306123B117EA5261006591D4 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 306123AB17EA5261006591D4 /* metamacros.h */; }; - 306123B217EA5261006591D4 /* metamacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 306123AB17EA5261006591D4 /* metamacros.h */; }; 307623AB17C6C8BD00E2CDF1 /* NSArray+StringArraySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 307623AA17C6C8BD00E2CDF1 /* NSArray+StringArraySpec.m */; }; 30865A91167F503400B1AB6E /* GTDiffSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 30865A90167F503400B1AB6E /* GTDiffSpec.m */; }; 30A269AD17B4878C000FE64E /* GTRepository+StatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 30A269AC17B4878C000FE64E /* GTRepository+StatusSpec.m */; }; 30A3D6541667F11C00C49A39 /* GTDiff.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A3D6521667F11C00C49A39 /* GTDiff.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 30A3D6551667F11C00C49A39 /* GTDiff.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A3D6521667F11C00C49A39 /* GTDiff.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30A3D6561667F11C00C49A39 /* GTDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 30A3D6531667F11C00C49A39 /* GTDiff.m */; }; - 30A3D6571667F11C00C49A39 /* GTDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 30A3D6531667F11C00C49A39 /* GTDiff.m */; }; - 30B1E7EE1703522100D0814D /* NSDate+GTTimeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B1E7EC1703522100D0814D /* NSDate+GTTimeAdditions.h */; }; - 30B1E7EF1703522100D0814D /* NSDate+GTTimeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B1E7EC1703522100D0814D /* NSDate+GTTimeAdditions.h */; }; 30B1E7F01703522100D0814D /* NSDate+GTTimeAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B1E7ED1703522100D0814D /* NSDate+GTTimeAdditions.m */; }; - 30B1E7F11703522100D0814D /* NSDate+GTTimeAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B1E7ED1703522100D0814D /* NSDate+GTTimeAdditions.m */; }; 30B1E8001703871900D0814D /* GTTimeAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B1E7FF1703871900D0814D /* GTTimeAdditionsSpec.m */; }; 30DCBA5C17B45213009B0EBD /* GTStatusDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA5A17B45213009B0EBD /* GTStatusDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 30DCBA5D17B45213009B0EBD /* GTStatusDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA5A17B45213009B0EBD /* GTStatusDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30DCBA5E17B45213009B0EBD /* GTStatusDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA5B17B45213009B0EBD /* GTStatusDelta.m */; }; - 30DCBA5F17B45213009B0EBD /* GTStatusDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA5B17B45213009B0EBD /* GTStatusDelta.m */; }; - 30DCBA6017B45213009B0EBD /* GTStatusDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA5B17B45213009B0EBD /* GTStatusDelta.m */; }; 30DCBA6317B45A78009B0EBD /* GTRepository+Status.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA6117B45A78009B0EBD /* GTRepository+Status.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 30DCBA6417B45A78009B0EBD /* GTRepository+Status.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA6117B45A78009B0EBD /* GTRepository+Status.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30DCBA6517B45A78009B0EBD /* GTRepository+Status.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA6217B45A78009B0EBD /* GTRepository+Status.m */; }; - 30DCBA6617B45A78009B0EBD /* GTRepository+Status.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA6217B45A78009B0EBD /* GTRepository+Status.m */; }; - 30DCBA6717B45A78009B0EBD /* GTRepository+Status.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA6217B45A78009B0EBD /* GTRepository+Status.m */; }; 30DCBA7317B4791A009B0EBD /* NSArray+StringArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA7017B4791A009B0EBD /* NSArray+StringArray.m */; }; - 30DCBA7417B4791A009B0EBD /* NSArray+StringArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA7017B4791A009B0EBD /* NSArray+StringArray.m */; }; - 30DCBA7517B4791A009B0EBD /* NSArray+StringArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA7017B4791A009B0EBD /* NSArray+StringArray.m */; }; 30FDC07F16835A8100654BF0 /* GTDiffLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 30FDC07D16835A8100654BF0 /* GTDiffLine.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 30FDC08016835A8100654BF0 /* GTDiffLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 30FDC07D16835A8100654BF0 /* GTDiffLine.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30FDC08116835A8100654BF0 /* GTDiffLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 30FDC07E16835A8100654BF0 /* GTDiffLine.m */; }; - 30FDC08216835A8100654BF0 /* GTDiffLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 30FDC07E16835A8100654BF0 /* GTDiffLine.m */; }; - 398F8AA6183111080071359D /* GTCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D79C0EC17DF9F4D00997DE4 /* GTCredential.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 398F8AA7183111260071359D /* GTCredential.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */; }; - 3E0A23E5159E0FDB00A6068F /* GTObjectDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C8054C13861F34004DCB0F /* GTObjectDatabase.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4D103ADD1819CFAA0029DB24 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D103ADC1819CFAA0029DB24 /* libiconv.dylib */; }; 4D123240178E009E0048F785 /* GTRepositoryCommittingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D12323F178E009E0048F785 /* GTRepositoryCommittingSpec.m */; }; 4D1C40D8182C006D00BE2960 /* GTBlobSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D1C40D7182C006D00BE2960 /* GTBlobSpec.m */; }; 4D79C0EE17DF9F4D00997DE4 /* GTCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D79C0EC17DF9F4D00997DE4 /* GTCredential.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4D79C0EF17DF9F4D00997DE4 /* GTCredential.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */; }; - 4D79C0F717DFAA7100997DE4 /* GTCredential+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D79C0F617DFAA7100997DE4 /* GTCredential+Private.h */; }; - 4DE864351794A37E00371A65 /* GTRepository+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DE864341794A37E00371A65 /* GTRepository+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 4DE864361794A37E00371A65 /* GTRepository+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DE864341794A37E00371A65 /* GTRepository+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 55C8054F13861FE7004DCB0F /* GTObjectDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */; }; + 4D9BCD25206D84B2003CD3CE /* libgit2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D9BCD23206D84AD003CD3CE /* libgit2.a */; }; + 4DB9711D2645CA3700D14944 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DB970A32645C8BB00D14944 /* Quick.framework */; }; + 4DB9711F2645CA5200D14944 /* ZipArchive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DB971172645C8F300D14944 /* ZipArchive.framework */; }; + 4DB971202645CA5D00D14944 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DB970AD2645C8BB00D14944 /* Quick.framework */; }; + 4DB971212645CA6600D14944 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DB971062645C8F300D14944 /* Nimble.framework */; }; + 4DB971222645CA6D00D14944 /* ZipArchive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DB971152645C8F300D14944 /* ZipArchive.framework */; }; + 4DBA4A3217DA73CE006CD5F5 /* GTRemoteSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */; }; + 4DC55AE51AD859AD0032563C /* GTCheckoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4DC55AE61AD859AD0032563C /* GTCheckoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4DC55AE71AD859AD0032563C /* GTCheckoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */; }; + 4DC55AE81AD859AD0032563C /* GTCheckoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */; }; + 4DD769A92645D214007599B8 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DB971022645C8F300D14944 /* Nimble.framework */; }; + 4DD7422C25D3F8CB009D9A17 /* libgit2-mac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DD7422B25D3F8CB009D9A17 /* libgit2-mac.a */; }; + 4DFFB15B183AA8D600D1565E /* GTRepository+RemoteOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DFFB159183AA8D600D1565E /* GTRepository+RemoteOperations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4DFFB15C183AA8D600D1565E /* GTRepository+RemoteOperations.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFFB15A183AA8D600D1565E /* GTRepository+RemoteOperations.m */; }; 55C8055013861FE7004DCB0F /* GTObjectDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */; }; 55C8057A13875578004DCB0F /* NSString+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8057313874CDF004DCB0F /* NSString+Git.m */; }; - 55C8057C13875579004DCB0F /* NSString+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8057313874CDF004DCB0F /* NSString+Git.m */; }; 55C8057D13875C11004DCB0F /* NSData+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C2266131459E700992935 /* NSData+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55C8057E13875C1B004DCB0F /* NSString+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C8057213874CDF004DCB0F /* NSString+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 55C8057F13875C62004DCB0F /* NSData+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C2266131459E700992935 /* NSData+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 55C8058013875C6E004DCB0F /* NSString+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C8057213874CDF004DCB0F /* NSString+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5BE612881745EE3400266D8C /* GTTreeBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BE612861745EE3300266D8C /* GTTreeBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5BE612891745EE3400266D8C /* GTTreeBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BE612861745EE3300266D8C /* GTTreeBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5BE6128A1745EE3400266D8C /* GTTreeBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE612871745EE3300266D8C /* GTTreeBuilder.m */; }; - 5BE6128B1745EE3400266D8C /* GTTreeBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE612871745EE3300266D8C /* GTTreeBuilder.m */; }; 5BE612931745EEBC00266D8C /* GTTreeBuilderSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE612921745EEBC00266D8C /* GTTreeBuilderSpec.m */; }; - 6A1F2FD517C6A8F3003DFADE /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A1F2FD317C6A8F3003DFADE /* libcrypto.a */; }; - 6A1F2FD617C6A8F3003DFADE /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A1F2FD417C6A8F3003DFADE /* libssl.a */; }; - 6A74CA3216A9429700E1A3C5 /* GTRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 883CD6AA1600EBC600F57354 /* GTRemote.m */; }; - 6A74CA3416A942AA00E1A3C5 /* NSData+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C2267131459E700992935 /* NSData+Git.m */; }; - 6A74CA3616A942C000E1A3C5 /* GTConfiguration+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 883CD6AE1600F01000F57354 /* GTConfiguration+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 6A74CA3716A942C800E1A3C5 /* GTRemote.h in Headers */ = {isa = PBXBuildFile; fileRef = 883CD6A91600EBC600F57354 /* GTRemote.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6A74CA3816A9432A00E1A3C5 /* ObjectiveGit.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AC51601209A00B7AD1D /* ObjectiveGit.m */; }; + 6EEB51A1199D62B9001D72C0 /* GTFetchHeadEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EEB519F199D62B9001D72C0 /* GTFetchHeadEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6EEB51A2199D62B9001D72C0 /* GTFetchHeadEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EEB51A0199D62B9001D72C0 /* GTFetchHeadEntry.m */; }; 79262F1013C697C100A4B1EA /* git2 in Copy git2 Headers */ = {isa = PBXBuildFile; fileRef = 79262F0E13C697BE00A4B1EA /* git2 */; }; 79262F8B13C69B1600A4B1EA /* git2.h in Headers */ = {isa = PBXBuildFile; fileRef = 79262F8A13C69B1600A4B1EA /* git2.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8803DA871313145700E6E818 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8803DA861313145700E6E818 /* libz.dylib */; }; 880EE66118AE700500B82455 /* GTFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 880EE65F18AE700500B82455 /* GTFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 880EE66218AE700500B82455 /* GTFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 880EE65F18AE700500B82455 /* GTFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 880EE66318AE700500B82455 /* GTFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 880EE66018AE700500B82455 /* GTFilter.m */; }; - 880EE66418AE700500B82455 /* GTFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 880EE66018AE700500B82455 /* GTFilter.m */; }; 882154691714740500D76B76 /* GTReflog.h in Headers */ = {isa = PBXBuildFile; fileRef = 882154671714740500D76B76 /* GTReflog.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8821546A1714740500D76B76 /* GTReflog.h in Headers */ = {isa = PBXBuildFile; fileRef = 882154671714740500D76B76 /* GTReflog.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8821546B1714740500D76B76 /* GTReflog.m in Sources */ = {isa = PBXBuildFile; fileRef = 882154681714740500D76B76 /* GTReflog.m */; }; - 8821546C1714740500D76B76 /* GTReflog.m in Sources */ = {isa = PBXBuildFile; fileRef = 882154681714740500D76B76 /* GTReflog.m */; }; 8821547617147A5200D76B76 /* GTReflogEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8821547417147A5100D76B76 /* GTReflogEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8821547717147A5200D76B76 /* GTReflogEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8821547417147A5100D76B76 /* GTReflogEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8821547817147A5200D76B76 /* GTReflogEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 8821547517147A5200D76B76 /* GTReflogEntry.m */; }; - 8821547917147A5200D76B76 /* GTReflogEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 8821547517147A5200D76B76 /* GTReflogEntry.m */; }; 8821547D17147B3600D76B76 /* GTOID.h in Headers */ = {isa = PBXBuildFile; fileRef = 8821547B17147B3600D76B76 /* GTOID.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8821547E17147B3600D76B76 /* GTOID.h in Headers */ = {isa = PBXBuildFile; fileRef = 8821547B17147B3600D76B76 /* GTOID.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8821547F17147B3600D76B76 /* GTOID.m in Sources */ = {isa = PBXBuildFile; fileRef = 8821547C17147B3600D76B76 /* GTOID.m */; }; - 8821548017147B3600D76B76 /* GTOID.m in Sources */ = {isa = PBXBuildFile; fileRef = 8821547C17147B3600D76B76 /* GTOID.m */; }; 88215483171499BE00D76B76 /* GTReflogSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88215482171499BE00D76B76 /* GTReflogSpec.m */; }; 88234B2618F2FE260039972E /* GTRepositoryResetSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88234B2518F2FE260039972E /* GTRepositoryResetSpec.m */; }; 8832811F173D8816006D7DCF /* GTIndexSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8832811E173D8816006D7DCF /* GTIndexSpec.m */; }; 88328128173D8A64006D7DCF /* GTTreeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88328127173D8A64006D7DCF /* GTTreeSpec.m */; }; 883CD6AB1600EBC600F57354 /* GTRemote.h in Headers */ = {isa = PBXBuildFile; fileRef = 883CD6A91600EBC600F57354 /* GTRemote.h */; settings = {ATTRIBUTES = (Public, ); }; }; 883CD6AC1600EBC600F57354 /* GTRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 883CD6AA1600EBC600F57354 /* GTRemote.m */; }; + 884C8A3A19FF4B890017E98D /* EXTScope.m in Sources */ = {isa = PBXBuildFile; fileRef = 306123AA17EA5261006591D4 /* EXTScope.m */; }; 886E622A18AEBF75000611A0 /* GTFilterSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 886E622818AEBF75000611A0 /* GTFilterSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 886E622B18AEBF75000611A0 /* GTFilterSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 886E622818AEBF75000611A0 /* GTFilterSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 886E622C18AEBF75000611A0 /* GTFilterSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 886E622918AEBF75000611A0 /* GTFilterSource.m */; }; - 886E622D18AEBF75000611A0 /* GTFilterSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 886E622918AEBF75000611A0 /* GTFilterSource.m */; }; 886E623718AECD86000611A0 /* GTFilterSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 886E623618AECD86000611A0 /* GTFilterSpec.m */; }; + 8870390B1975E3F2004118D7 /* GTDiffDeltaSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8870390A1975E3F2004118D7 /* GTDiffDeltaSpec.m */; }; 88746CC417FA1C950005888A /* GTRepository+Committing.h in Headers */ = {isa = PBXBuildFile; fileRef = 88746CC217FA1C950005888A /* GTRepository+Committing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 88746CC517FA1C950005888A /* GTRepository+Committing.h in Headers */ = {isa = PBXBuildFile; fileRef = 88746CC217FA1C950005888A /* GTRepository+Committing.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88746CC617FA1C950005888A /* GTRepository+Committing.m in Sources */ = {isa = PBXBuildFile; fileRef = 88746CC317FA1C950005888A /* GTRepository+Committing.m */; }; - 88746CC717FA1C950005888A /* GTRepository+Committing.m in Sources */ = {isa = PBXBuildFile; fileRef = 88746CC317FA1C950005888A /* GTRepository+Committing.m */; }; 88948AC91779243600809CDA /* GTObjectDatabaseSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88948AC81779243600809CDA /* GTObjectDatabaseSpec.m */; }; + 889923FB19FF5DD40092A9A6 /* git2 in Headers */ = {isa = PBXBuildFile; fileRef = 889923F919FF5DD40092A9A6 /* git2 */; settings = {ATTRIBUTES = (Public, ); }; }; 88A994BA16FCE7D400402C7B /* GTBranchSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A994B916FCE7D400402C7B /* GTBranchSpec.m */; }; - 88A994CB16FCED1D00402C7B /* GTTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A994CA16FCED1D00402C7B /* GTTestCase.m */; }; + 88A994CB16FCED1D00402C7B /* QuickSpec+GTFixtures.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A994CA16FCED1D00402C7B /* QuickSpec+GTFixtures.m */; }; + 88B2131C1B20E785005CF2C5 /* GTRepository+References.h in Headers */ = {isa = PBXBuildFile; fileRef = 88B2131A1B20E785005CF2C5 /* GTRepository+References.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88B2131D1B20E785005CF2C5 /* GTRepository+References.h in Headers */ = {isa = PBXBuildFile; fileRef = 88B2131A1B20E785005CF2C5 /* GTRepository+References.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88B2131E1B20E785005CF2C5 /* GTRepository+References.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B2131B1B20E785005CF2C5 /* GTRepository+References.m */; }; + 88B2131F1B20E785005CF2C5 /* GTRepository+References.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B2131B1B20E785005CF2C5 /* GTRepository+References.m */; }; 88BC0E5018EF4F3600C7D0E6 /* GTRepository+Reset.h in Headers */ = {isa = PBXBuildFile; fileRef = 88BC0E4E18EF4F3600C7D0E6 /* GTRepository+Reset.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 88BC0E5118EF4F3600C7D0E6 /* GTRepository+Reset.h in Headers */ = {isa = PBXBuildFile; fileRef = 88BC0E4E18EF4F3600C7D0E6 /* GTRepository+Reset.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88BC0E5218EF4F3600C7D0E6 /* GTRepository+Reset.m in Sources */ = {isa = PBXBuildFile; fileRef = 88BC0E4F18EF4F3600C7D0E6 /* GTRepository+Reset.m */; }; - 88BC0E5318EF4F3600C7D0E6 /* GTRepository+Reset.m in Sources */ = {isa = PBXBuildFile; fileRef = 88BC0E4F18EF4F3600C7D0E6 /* GTRepository+Reset.m */; }; 88C0BC5917038CF3009E99AA /* GTConfigurationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C0BC5817038CF3009E99AA /* GTConfigurationSpec.m */; }; + 88E353001982E9160051001F /* GTRepository+Attributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 88E352FE1982E9160051001F /* GTRepository+Attributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88E353021982E9160051001F /* GTRepository+Attributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E352FF1982E9160051001F /* GTRepository+Attributes.m */; }; + 88E353061982EA6B0051001F /* GTRepositoryAttributesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E353051982EA6B0051001F /* GTRepositoryAttributesSpec.m */; }; 88EB7E4D14AEBA600046FEA4 /* GTConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EB7E4B14AEBA600046FEA4 /* GTConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88EB7E4E14AEBA600046FEA4 /* GTConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EB7E4C14AEBA600046FEA4 /* GTConfiguration.m */; }; - 88F05A9D16011F6A00B7AD1D /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88F05A9C16011F6A00B7AD1D /* SenTestingKit.framework */; }; 88F05A9E16011F6E00B7AD1D /* ObjectiveGit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* ObjectiveGit.framework */; }; 88F05AB516011FFD00B7AD1D /* GTCommitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AA416011FFD00B7AD1D /* GTCommitSpec.m */; }; - 88F05AB916011FFD00B7AD1D /* GTObjectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AA816011FFD00B7AD1D /* GTObjectTest.m */; }; - 88F05AC41601204200B7AD1D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 88F05A7816011E5400B7AD1D /* InfoPlist.strings */; }; + 88F05AB916011FFD00B7AD1D /* GTObjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AA816011FFD00B7AD1D /* GTObjectSpec.m */; }; 88F05AC61601209A00B7AD1D /* ObjectiveGit.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AC51601209A00B7AD1D /* ObjectiveGit.m */; }; - 88F05AC716012CE500B7AD1D /* NSString+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8057313874CDF004DCB0F /* NSString+Git.m */; }; - 88F05AC816012CEE00B7AD1D /* NSData+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C2267131459E700992935 /* NSData+Git.m */; }; 88F50F59132054D800584FBE /* GTBranch.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F50F56132054D800584FBE /* GTBranch.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88F50F5A132054D800584FBE /* GTBranch.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F50F57132054D800584FBE /* GTBranch.m */; }; 88F6D9D91320451F00CC0BA8 /* ObjectiveGit.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F6D9D81320451F00CC0BA8 /* ObjectiveGit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88F6D9FA1320467100CC0BA8 /* GTCommit.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C22A41314609A00992935 /* GTCommit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88F6D9FB1320467500CC0BA8 /* GTObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C22A71314625800992935 /* GTObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; 88F6D9FC1320467800CC0BA8 /* GTSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C254313148DC900992935 /* GTSignature.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; AA046112134F4D2000DF526B /* GTOdbObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AA046110134F4D2000DF526B /* GTOdbObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AA046113134F4D2000DF526B /* GTOdbObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AA046110134F4D2000DF526B /* GTOdbObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; AA046114134F4D2000DF526B /* GTOdbObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AA046111134F4D2000DF526B /* GTOdbObject.m */; }; - AA046115134F4D2000DF526B /* GTOdbObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AA046111134F4D2000DF526B /* GTOdbObject.m */; }; BD441E08131ED0C300187010 /* GTReference.h in Headers */ = {isa = PBXBuildFile; fileRef = BD441E06131ED0C300187010 /* GTReference.h */; settings = {ATTRIBUTES = (Public, ); }; }; BD441E09131ED0C300187010 /* GTReference.m in Sources */ = {isa = PBXBuildFile; fileRef = BD441E07131ED0C300187010 /* GTReference.m */; }; BD6B0411131496B8001909D0 /* GTTree.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6B040F131496B8001909D0 /* GTTree.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -262,54 +191,405 @@ BDFAF9C4131C1845000508BC /* GTIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = BDFAF9C2131C1845000508BC /* GTIndex.m */; }; BDFAF9C9131C1868000508BC /* GTIndexEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = BDFAF9C7131C1868000508BC /* GTIndexEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; BDFAF9CA131C1868000508BC /* GTIndexEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = BDFAF9C8131C1868000508BC /* GTIndexEntry.m */; }; + BEF7E4E01A3A47690035BB8E /* git2 in Copy git2 Headers again */ = {isa = PBXBuildFile; fileRef = 79262F0E13C697BE00A4B1EA /* git2 */; }; D00F6816175D373C004DB9D6 /* GTReferenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D00F6815175D373C004DB9D6 /* GTReferenceSpec.m */; }; D015F7CA17F695E800AD5E1F /* GTRepository+Stashing.h in Headers */ = {isa = PBXBuildFile; fileRef = D015F7C817F695E800AD5E1F /* GTRepository+Stashing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D015F7CB17F695E800AD5E1F /* GTRepository+Stashing.h in Headers */ = {isa = PBXBuildFile; fileRef = D015F7C817F695E800AD5E1F /* GTRepository+Stashing.h */; settings = {ATTRIBUTES = (Public, ); }; }; D015F7CC17F695E800AD5E1F /* GTRepository+Stashing.m in Sources */ = {isa = PBXBuildFile; fileRef = D015F7C917F695E800AD5E1F /* GTRepository+Stashing.m */; }; - D015F7CD17F695E800AD5E1F /* GTRepository+Stashing.m in Sources */ = {isa = PBXBuildFile; fileRef = D015F7C917F695E800AD5E1F /* GTRepository+Stashing.m */; }; D015F7D517F6965400AD5E1F /* GTRepositoryStashingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */; }; + D019778A19F8307600F523DA /* ObjectiveGit.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AC51601209A00B7AD1D /* ObjectiveGit.m */; }; + D01B6F1419F82F6000D411BC /* git2.h in Headers */ = {isa = PBXBuildFile; fileRef = 79262F8A13C69B1600A4B1EA /* git2.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F1519F82F7B00D411BC /* NSData+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C2266131459E700992935 /* NSData+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F1619F82F7B00D411BC /* NSData+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C2267131459E700992935 /* NSData+Git.m */; }; + D01B6F1719F82F7B00D411BC /* NSError+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = BDE4C060130EFE2C00851650 /* NSError+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F1819F82F7B00D411BC /* NSError+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = BDE4C061130EFE2C00851650 /* NSError+Git.m */; }; + D01B6F1919F82F7B00D411BC /* NSString+Git.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C8057213874CDF004DCB0F /* NSString+Git.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F1A19F82F7B00D411BC /* NSString+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8057313874CDF004DCB0F /* NSString+Git.m */; }; + D01B6F1B19F82F7B00D411BC /* NSDate+GTTimeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B1E7EC1703522100D0814D /* NSDate+GTTimeAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F1C19F82F7B00D411BC /* NSDate+GTTimeAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B1E7ED1703522100D0814D /* NSDate+GTTimeAdditions.m */; }; + D01B6F1D19F82F7B00D411BC /* NSArray+StringArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA6F17B4791A009B0EBD /* NSArray+StringArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F1E19F82F7B00D411BC /* NSArray+StringArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA7017B4791A009B0EBD /* NSArray+StringArray.m */; }; + D01B6F1F19F82F8700D411BC /* GTRepository.h in Headers */ = {isa = PBXBuildFile; fileRef = BDE4C062130EFE2C00851650 /* GTRepository.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2019F82F8700D411BC /* GTRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = BDE4C063130EFE2C00851650 /* GTRepository.m */; }; + D01B6F2119F82F8700D411BC /* GTRepository+Status.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA6117B45A78009B0EBD /* GTRepository+Status.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2219F82F8700D411BC /* GTRepository+Status.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA6217B45A78009B0EBD /* GTRepository+Status.m */; }; + D01B6F2319F82F8700D411BC /* GTRepository+Reset.h in Headers */ = {isa = PBXBuildFile; fileRef = 88BC0E4E18EF4F3600C7D0E6 /* GTRepository+Reset.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2419F82F8700D411BC /* GTRepository+Reset.m in Sources */ = {isa = PBXBuildFile; fileRef = 88BC0E4F18EF4F3600C7D0E6 /* GTRepository+Reset.m */; }; + D01B6F2519F82F8700D411BC /* GTStatusDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA5A17B45213009B0EBD /* GTStatusDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2619F82F8700D411BC /* GTStatusDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 30DCBA5B17B45213009B0EBD /* GTStatusDelta.m */; }; + D01B6F2719F82F8700D411BC /* GTRepository+Stashing.h in Headers */ = {isa = PBXBuildFile; fileRef = D015F7C817F695E800AD5E1F /* GTRepository+Stashing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2819F82F8700D411BC /* GTRepository+Stashing.m in Sources */ = {isa = PBXBuildFile; fileRef = D015F7C917F695E800AD5E1F /* GTRepository+Stashing.m */; }; + D01B6F2919F82F8700D411BC /* GTRepository+Committing.h in Headers */ = {isa = PBXBuildFile; fileRef = 88746CC217FA1C950005888A /* GTRepository+Committing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2A19F82F8700D411BC /* GTRepository+Committing.m in Sources */ = {isa = PBXBuildFile; fileRef = 88746CC317FA1C950005888A /* GTRepository+Committing.m */; }; + D01B6F2B19F82F8700D411BC /* GTRepository+RemoteOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DFFB159183AA8D600D1565E /* GTRepository+RemoteOperations.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2C19F82F8700D411BC /* GTRepository+RemoteOperations.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFFB15A183AA8D600D1565E /* GTRepository+RemoteOperations.m */; }; + D01B6F2D19F82F8700D411BC /* GTEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD8AE6D13131B8800CB5D40 /* GTEnumerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F2E19F82F8700D411BC /* GTEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD8AE6E13131B8800CB5D40 /* GTEnumerator.m */; }; + D01B6F2F19F82F8700D411BC /* GTObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C22A71314625800992935 /* GTObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3019F82F8700D411BC /* GTObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C22A81314625800992935 /* GTObject.m */; }; + D01B6F3119F82F8700D411BC /* GTCommit.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C22A41314609A00992935 /* GTCommit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3219F82F8700D411BC /* GTCommit.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C22A51314609A00992935 /* GTCommit.m */; }; + D01B6F3319F82F8700D411BC /* GTSignature.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6C254313148DC900992935 /* GTSignature.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3419F82F8700D411BC /* GTSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C254413148DC900992935 /* GTSignature.m */; }; + D01B6F3519F82F8700D411BC /* GTBlob.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD627971318391200DE34D1 /* GTBlob.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3619F82F8700D411BC /* GTBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD627981318391200DE34D1 /* GTBlob.m */; }; + D01B6F3719F82F8700D411BC /* GTTree.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6B040F131496B8001909D0 /* GTTree.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3819F82F8700D411BC /* GTTree.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6B0410131496B8001909D0 /* GTTree.m */; }; + D01B6F3919F82F8700D411BC /* GTTreeEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6B0415131496CC001909D0 /* GTTreeEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3A19F82F8700D411BC /* GTTreeEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6B0416131496CC001909D0 /* GTTreeEntry.m */; }; + D01B6F3B19F82F8700D411BC /* GTTreeBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BE612861745EE3300266D8C /* GTTreeBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3C19F82F8700D411BC /* GTTreeBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE612871745EE3300266D8C /* GTTreeBuilder.m */; }; + D01B6F3D19F82F8700D411BC /* GTTag.h in Headers */ = {isa = PBXBuildFile; fileRef = BDD62922131C03D600DE34D1 /* GTTag.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F3E19F82F8700D411BC /* GTTag.m in Sources */ = {isa = PBXBuildFile; fileRef = BDD62923131C03D600DE34D1 /* GTTag.m */; }; + D01B6F3F19F82F8700D411BC /* GTIndex.h in Headers */ = {isa = PBXBuildFile; fileRef = BDFAF9C1131C1845000508BC /* GTIndex.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4019F82F8700D411BC /* GTIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = BDFAF9C2131C1845000508BC /* GTIndex.m */; }; + D01B6F4119F82F8700D411BC /* GTIndexEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = BDFAF9C7131C1868000508BC /* GTIndexEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4219F82F8700D411BC /* GTIndexEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = BDFAF9C8131C1868000508BC /* GTIndexEntry.m */; }; + D01B6F4319F82F8700D411BC /* GTReference.h in Headers */ = {isa = PBXBuildFile; fileRef = BD441E06131ED0C300187010 /* GTReference.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4419F82F8700D411BC /* GTReference.m in Sources */ = {isa = PBXBuildFile; fileRef = BD441E07131ED0C300187010 /* GTReference.m */; }; + D01B6F4519F82F8700D411BC /* GTBranch.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F50F56132054D800584FBE /* GTBranch.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4619F82F8700D411BC /* GTBranch.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F50F57132054D800584FBE /* GTBranch.m */; }; + D01B6F4719F82F8700D411BC /* GTObjectDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 55C8054C13861F34004DCB0F /* GTObjectDatabase.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4819F82F8700D411BC /* GTObjectDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */; }; + D01B6F4919F82F8700D411BC /* GTOdbObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AA046110134F4D2000DF526B /* GTOdbObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4A19F82F8700D411BC /* GTOdbObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AA046111134F4D2000DF526B /* GTOdbObject.m */; }; + D01B6F4B19F82F8700D411BC /* GTConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EB7E4B14AEBA600046FEA4 /* GTConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4C19F82F8700D411BC /* GTConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EB7E4C14AEBA600046FEA4 /* GTConfiguration.m */; }; + D01B6F4D19F82F8700D411BC /* GTRemote.h in Headers */ = {isa = PBXBuildFile; fileRef = 883CD6A91600EBC600F57354 /* GTRemote.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F4E19F82F8700D411BC /* GTRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 883CD6AA1600EBC600F57354 /* GTRemote.m */; }; + D01B6F4F19F82FA600D411BC /* GTRepository+Blame.h in Headers */ = {isa = PBXBuildFile; fileRef = 20F43DE118A2F667007D3621 /* GTRepository+Blame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5019F82FA600D411BC /* GTRepository+Blame.m in Sources */ = {isa = PBXBuildFile; fileRef = 20F43DE218A2F667007D3621 /* GTRepository+Blame.m */; }; + D01B6F5119F82FA600D411BC /* GTBlame.h in Headers */ = {isa = PBXBuildFile; fileRef = DD3D9510182A81E1004AF532 /* GTBlame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5219F82FA600D411BC /* GTBlame.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3D9511182A81E1004AF532 /* GTBlame.m */; }; + D01B6F5319F82FA600D411BC /* GTBlameHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = DD3D951A182AB25C004AF532 /* GTBlameHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5419F82FA600D411BC /* GTBlameHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3D951B182AB25C004AF532 /* GTBlameHunk.m */; }; + D01B6F5519F82FA600D411BC /* GTReflog.h in Headers */ = {isa = PBXBuildFile; fileRef = 882154671714740500D76B76 /* GTReflog.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5619F82FA600D411BC /* GTReflog.m in Sources */ = {isa = PBXBuildFile; fileRef = 882154681714740500D76B76 /* GTReflog.m */; }; + D01B6F5719F82FA600D411BC /* GTReflogEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8821547417147A5100D76B76 /* GTReflogEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5819F82FA600D411BC /* GTReflogEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 8821547517147A5200D76B76 /* GTReflogEntry.m */; }; + D01B6F5919F82FA600D411BC /* GTOID.h in Headers */ = {isa = PBXBuildFile; fileRef = 8821547B17147B3600D76B76 /* GTOID.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5A19F82FA600D411BC /* GTOID.m in Sources */ = {isa = PBXBuildFile; fileRef = 8821547C17147B3600D76B76 /* GTOID.m */; }; + D01B6F5B19F82FA600D411BC /* GTSubmodule.h in Headers */ = {isa = PBXBuildFile; fileRef = D09C2E341755F16200065E36 /* GTSubmodule.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5C19F82FA600D411BC /* GTSubmodule.m in Sources */ = {isa = PBXBuildFile; fileRef = D09C2E351755F16200065E36 /* GTSubmodule.m */; }; + D01B6F5D19F82FA600D411BC /* GTCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D79C0EC17DF9F4D00997DE4 /* GTCredential.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F5E19F82FA600D411BC /* GTCredential.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */; }; + D01B6F5F19F82FA600D411BC /* GTFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 880EE65F18AE700500B82455 /* GTFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F6019F82FA600D411BC /* GTFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 880EE66018AE700500B82455 /* GTFilter.m */; }; + D01B6F6119F82FA600D411BC /* GTFilterSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 886E622818AEBF75000611A0 /* GTFilterSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F6219F82FA600D411BC /* GTFilterSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 886E622918AEBF75000611A0 /* GTFilterSource.m */; }; + D01B6F6319F82FA600D411BC /* GTFilterList.h in Headers */ = {isa = PBXBuildFile; fileRef = D0CE551E18B6C58F008EB8E0 /* GTFilterList.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F6419F82FA600D411BC /* GTFilterList.m in Sources */ = {isa = PBXBuildFile; fileRef = D0CE551F18B6C58F008EB8E0 /* GTFilterList.m */; }; + D01B6F6519F82FA600D411BC /* GTRepository+Attributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 88E352FE1982E9160051001F /* GTRepository+Attributes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F6619F82FA600D411BC /* GTRepository+Attributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E352FF1982E9160051001F /* GTRepository+Attributes.m */; }; + D01B6F6719F82FA600D411BC /* GTFetchHeadEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EEB519F199D62B9001D72C0 /* GTFetchHeadEntry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F6819F82FA600D411BC /* GTFetchHeadEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EEB51A0199D62B9001D72C0 /* GTFetchHeadEntry.m */; }; + D01B6F6B19F82FB300D411BC /* GTDiff.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A3D6521667F11C00C49A39 /* GTDiff.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F6C19F82FB300D411BC /* GTDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 30A3D6531667F11C00C49A39 /* GTDiff.m */; }; + D01B6F6D19F82FB300D411BC /* GTDiffFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8691668E48500CE3409 /* GTDiffFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F6E19F82FB300D411BC /* GTDiffFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D86A1668E48500CE3409 /* GTDiffFile.m */; }; + D01B6F6F19F82FB300D411BC /* GTDiffHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D86F1668E78500CE3409 /* GTDiffHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F7019F82FB300D411BC /* GTDiffHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8701668E78500CE3409 /* GTDiffHunk.m */; }; + D01B6F7119F82FB300D411BC /* GTDiffDelta.h in Headers */ = {isa = PBXBuildFile; fileRef = 3011D8751668F29600CE3409 /* GTDiffDelta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F7219F82FB300D411BC /* GTDiffDelta.m in Sources */ = {isa = PBXBuildFile; fileRef = 3011D8761668F29600CE3409 /* GTDiffDelta.m */; }; + D01B6F7319F82FB300D411BC /* GTDiffLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 30FDC07D16835A8100654BF0 /* GTDiffLine.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F7419F82FB300D411BC /* GTDiffLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 30FDC07E16835A8100654BF0 /* GTDiffLine.m */; }; + D01B6F7519F82FB300D411BC /* GTDiffPatch.h in Headers */ = {isa = PBXBuildFile; fileRef = D03B579F18BFFF07007124F4 /* GTDiffPatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01B6F7619F82FB300D411BC /* GTDiffPatch.m in Sources */ = {isa = PBXBuildFile; fileRef = D03B57A018BFFF07007124F4 /* GTDiffPatch.m */; }; + D01EFDA0195DEF2200838D24 /* NSDataGitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D01EFD9F195DEF2200838D24 /* NSDataGitSpec.m */; }; D021DF4F1806899000934E32 /* NSArray+StringArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA6F17B4791A009B0EBD /* NSArray+StringArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D021DF501806899200934E32 /* NSArray+StringArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DCBA6F17B4791A009B0EBD /* NSArray+StringArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D03B07F71965DAB0009E5624 /* NSData+Git.m in Sources */ = {isa = PBXBuildFile; fileRef = BD6C2267131459E700992935 /* NSData+Git.m */; }; D03B57A118BFFF07007124F4 /* GTDiffPatch.h in Headers */ = {isa = PBXBuildFile; fileRef = D03B579F18BFFF07007124F4 /* GTDiffPatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D03B57A218BFFF07007124F4 /* GTDiffPatch.h in Headers */ = {isa = PBXBuildFile; fileRef = D03B579F18BFFF07007124F4 /* GTDiffPatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; D03B57A418BFFF07007124F4 /* GTDiffPatch.m in Sources */ = {isa = PBXBuildFile; fileRef = D03B57A018BFFF07007124F4 /* GTDiffPatch.m */; }; - D03B57A518BFFF07007124F4 /* GTDiffPatch.m in Sources */ = {isa = PBXBuildFile; fileRef = D03B57A018BFFF07007124F4 /* GTDiffPatch.m */; }; D03B7C411756AB370034A610 /* GTSubmoduleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03B7C401756AB370034A610 /* GTSubmoduleSpec.m */; }; D040AF70177B9779001AD9EB /* GTOIDSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D040AF6F177B9779001AD9EB /* GTOIDSpec.m */; }; D040AF78177B9A9E001AD9EB /* GTSignatureSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D040AF77177B9A9E001AD9EB /* GTSignatureSpec.m */; }; + D05FC5E219FAD039008C9348 /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A1F2FD317C6A8F3003DFADE /* libcrypto.a */; }; + D05FC5E319FAD03C008C9348 /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A1F2FD417C6A8F3003DFADE /* libssl.a */; }; + D05FC5E419FAD040008C9348 /* libssh2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A502B8617D6892D00BAF4A5 /* libssh2.a */; }; D06D9E011755D10000558C17 /* GTEnumeratorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D06D9E001755D10000558C17 /* GTEnumeratorSpec.m */; }; D0751CD918BE520400134314 /* GTFilterListSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0751CD818BE520400134314 /* GTFilterListSpec.m */; }; D09C2E361755F16200065E36 /* GTSubmodule.h in Headers */ = {isa = PBXBuildFile; fileRef = D09C2E341755F16200065E36 /* GTSubmodule.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D09C2E371755F16200065E36 /* GTSubmodule.h in Headers */ = {isa = PBXBuildFile; fileRef = D09C2E341755F16200065E36 /* GTSubmodule.h */; settings = {ATTRIBUTES = (Public, ); }; }; D09C2E381755F16200065E36 /* GTSubmodule.m in Sources */ = {isa = PBXBuildFile; fileRef = D09C2E351755F16200065E36 /* GTSubmodule.m */; }; - D09C2E391755F16200065E36 /* GTSubmodule.m in Sources */ = {isa = PBXBuildFile; fileRef = D09C2E351755F16200065E36 /* GTSubmodule.m */; }; D09C2E51175602A500065E36 /* fixtures.zip in Resources */ = {isa = PBXBuildFile; fileRef = D09C2E50175602A500065E36 /* fixtures.zip */; }; + D0A0129519F99EF8007F1914 /* NSDate+GTTimeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30B1E7EC1703522100D0814D /* NSDate+GTTimeAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0A0129719F9A660007F1914 /* SwiftSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A0129619F9A660007F1914 /* SwiftSpec.swift */; }; D0AC906C172F941F00347DC4 /* GTRepositorySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AC906B172F941F00347DC4 /* GTRepositorySpec.m */; }; D0CE552018B6C58F008EB8E0 /* GTFilterList.h in Headers */ = {isa = PBXBuildFile; fileRef = D0CE551E18B6C58F008EB8E0 /* GTFilterList.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D0CE552118B6C58F008EB8E0 /* GTFilterList.h in Headers */ = {isa = PBXBuildFile; fileRef = D0CE551E18B6C58F008EB8E0 /* GTFilterList.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0CE552318B6C58F008EB8E0 /* GTFilterList.m in Sources */ = {isa = PBXBuildFile; fileRef = D0CE551F18B6C58F008EB8E0 /* GTFilterList.m */; }; - D0CE552418B6C58F008EB8E0 /* GTFilterList.m in Sources */ = {isa = PBXBuildFile; fileRef = D0CE551F18B6C58F008EB8E0 /* GTFilterList.m */; }; + D0E0171519F9AD820019930C /* ObjectiveGit.h in Headers */ = {isa = PBXBuildFile; fileRef = 88F6D9D81320451F00CC0BA8 /* ObjectiveGit.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0F4E28A17C7F24200BBDE30 /* NSErrorGitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0F4E28917C7F24200BBDE30 /* NSErrorGitSpec.m */; }; - D4ACA56118DBA88E00EBD899 /* libSpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4ACA56018DBA88E00EBD899 /* libSpecta.a */; }; - D4ACA56318DBA89300EBD899 /* libExpecta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4ACA56218DBA89300EBD899 /* libExpecta.a */; }; DD3D9512182A81E1004AF532 /* GTBlame.h in Headers */ = {isa = PBXBuildFile; fileRef = DD3D9510182A81E1004AF532 /* GTBlame.h */; settings = {ATTRIBUTES = (Public, ); }; }; DD3D9513182A81E1004AF532 /* GTBlame.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3D9511182A81E1004AF532 /* GTBlame.m */; }; DD3D951C182AB25C004AF532 /* GTBlameHunk.h in Headers */ = {isa = PBXBuildFile; fileRef = DD3D951A182AB25C004AF532 /* GTBlameHunk.h */; settings = {ATTRIBUTES = (Public, ); }; }; DD3D951D182AB25C004AF532 /* GTBlameHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3D951B182AB25C004AF532 /* GTBlameHunk.m */; }; - DD3D951E182AB3BD004AF532 /* GTBlame.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3D9511182A81E1004AF532 /* GTBlame.m */; }; - DD3D9520182AB3C4004AF532 /* GTBlameHunk.m in Sources */ = {isa = PBXBuildFile; fileRef = DD3D951B182AB25C004AF532 /* GTBlameHunk.m */; }; - E9FFC6BF1577CC8300A9E736 /* GTConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EB7E4C14AEBA600046FEA4 /* GTConfiguration.m */; }; - E9FFC6C01577CC8A00A9E736 /* GTConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EB7E4B14AEBA600046FEA4 /* GTConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F6ED8DA1180E713200A32D40 /* GTRemoteSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F6ED8DA0180E713200A32D40 /* GTRemoteSpec.m */; }; + F81B6B50207B032800AB0836 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8D63871207AE62A00D1FD32 /* XCTest.framework */; }; + F81B6B59207B0D3B00AB0836 /* SwiftSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A0129619F9A660007F1914 /* SwiftSpec.swift */; }; + F84277BE207B104A008AB8E8 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8D63872207AE63A00D1FD32 /* XCTest.framework */; }; + F86949AA1BF1B79E00A989D3 /* GTUtilityFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = F8EFA0391B4059ED000FF7D0 /* GTUtilityFunctions.m */; }; + F879D8311B4B788F002D5C07 /* Libgit2FeaturesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F879D82F1B4B77F4002D5C07 /* Libgit2FeaturesSpec.m */; }; + F879D83C1B4B7F7D002D5C07 /* ObjectiveGit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01B6ED319F82E2000D411BC /* ObjectiveGit.framework */; }; + F879D8441B4B80C7002D5C07 /* Libgit2FeaturesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F879D82F1B4B77F4002D5C07 /* Libgit2FeaturesSpec.m */; }; + F8D007701B4F7CA8009A8DAF /* NSErrorGitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0F4E28917C7F24200BBDE30 /* NSErrorGitSpec.m */; }; + F8D007711B4F7CB0009A8DAF /* NSDataGitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D01EFD9F195DEF2200838D24 /* NSDataGitSpec.m */; }; + F8D007721B4F7CB6009A8DAF /* NSArray+StringArraySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 307623AA17C6C8BD00E2CDF1 /* NSArray+StringArraySpec.m */; }; + F8D007731B4F7CC3009A8DAF /* GTSignatureSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D040AF77177B9A9E001AD9EB /* GTSignatureSpec.m */; }; + F8D007741B4F7CCC009A8DAF /* GTOIDSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D040AF6F177B9779001AD9EB /* GTOIDSpec.m */; }; + F8D007761B4F7D10009A8DAF /* GTTimeAdditionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B1E7FF1703871900D0814D /* GTTimeAdditionsSpec.m */; }; + F8D0078C1B4FA03B009A8DAF /* GTBlobSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D1C40D7182C006D00BE2960 /* GTBlobSpec.m */; }; + F8D0078D1B4FA03B009A8DAF /* GTBranchSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A994B916FCE7D400402C7B /* GTBranchSpec.m */; }; + F8D0078E1B4FA03B009A8DAF /* GTCommitSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AA416011FFD00B7AD1D /* GTCommitSpec.m */; }; + F8D0078F1B4FA03B009A8DAF /* GTConfigurationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C0BC5817038CF3009E99AA /* GTConfigurationSpec.m */; }; + F8D007901B4FA03B009A8DAF /* GTDiffSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 30865A90167F503400B1AB6E /* GTDiffSpec.m */; }; + F8D007911B4FA03B009A8DAF /* GTEnumeratorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D06D9E001755D10000558C17 /* GTEnumeratorSpec.m */; }; + F8D007921B4FA03B009A8DAF /* GTIndexSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8832811E173D8816006D7DCF /* GTIndexSpec.m */; }; + F8D007931B4FA03B009A8DAF /* GTObjectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88F05AA816011FFD00B7AD1D /* GTObjectSpec.m */; }; + F8D007941B4FA03B009A8DAF /* GTReferenceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D00F6815175D373C004DB9D6 /* GTReferenceSpec.m */; }; + F8D007951B4FA03B009A8DAF /* GTReflogSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88215482171499BE00D76B76 /* GTReflogSpec.m */; }; + F8D007961B4FA03B009A8DAF /* GTRemotePushSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F8E4A2901A170CA6006485A8 /* GTRemotePushSpec.m */; }; + F8D007971B4FA03B009A8DAF /* GTRemoteSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */; }; + F8D007981B4FA03B009A8DAF /* GTBlameSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 200578C418932A82001C06C3 /* GTBlameSpec.m */; }; + F8D007991B4FA03B009A8DAF /* GTRepositorySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AC906B172F941F00347DC4 /* GTRepositorySpec.m */; }; + F8D0079A1B4FA03B009A8DAF /* GTRepositoryCommittingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D12323F178E009E0048F785 /* GTRepositoryCommittingSpec.m */; }; + F8D0079B1B4FA03B009A8DAF /* GTSubmoduleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D03B7C401756AB370034A610 /* GTSubmoduleSpec.m */; }; + F8D0079C1B4FA03B009A8DAF /* GTTagSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 2089E43B17D9A58000F451DA /* GTTagSpec.m */; }; + F8D0079D1B4FA03B009A8DAF /* GTTreeBuilderSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE612921745EEBC00266D8C /* GTTreeBuilderSpec.m */; }; + F8D0079E1B4FA03B009A8DAF /* GTTreeSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88328127173D8A64006D7DCF /* GTTreeSpec.m */; }; + F8D0079F1B4FA03B009A8DAF /* GTObjectDatabaseSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88948AC81779243600809CDA /* GTObjectDatabaseSpec.m */; }; + F8D007A01B4FA03B009A8DAF /* GTRepository+StatusSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 30A269AC17B4878C000FE64E /* GTRepository+StatusSpec.m */; }; + F8D007A11B4FA03B009A8DAF /* GTRepositoryStashingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */; }; + F8D007A21B4FA03B009A8DAF /* GTFilterSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 886E623618AECD86000611A0 /* GTFilterSpec.m */; }; + F8D007A31B4FA03B009A8DAF /* GTFilterListSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0751CD818BE520400134314 /* GTFilterListSpec.m */; }; + F8D007A41B4FA03B009A8DAF /* GTRepositoryResetSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88234B2518F2FE260039972E /* GTRepositoryResetSpec.m */; }; + F8D007A51B4FA03B009A8DAF /* GTDiffDeltaSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8870390A1975E3F2004118D7 /* GTDiffDeltaSpec.m */; }; + F8D007A61B4FA03B009A8DAF /* GTRepositoryAttributesSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E353051982EA6B0051001F /* GTRepositoryAttributesSpec.m */; }; + F8D007A71B4FA040009A8DAF /* QuickSpec+GTFixtures.m in Sources */ = {isa = PBXBuildFile; fileRef = 88A994CA16FCED1D00402C7B /* QuickSpec+GTFixtures.m */; }; + F8D007A81B4FA045009A8DAF /* fixtures.zip in Resources */ = {isa = PBXBuildFile; fileRef = D09C2E50175602A500065E36 /* fixtures.zip */; }; + F8D1BDEE1B31FE7C00CDEC90 /* GTRepository+Pull.h in Headers */ = {isa = PBXBuildFile; fileRef = F8D1BDEC1B31FE7C00CDEC90 /* GTRepository+Pull.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F8D1BDEF1B31FE7C00CDEC90 /* GTRepository+Pull.h in Headers */ = {isa = PBXBuildFile; fileRef = F8D1BDEC1B31FE7C00CDEC90 /* GTRepository+Pull.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F8D1BDF01B31FE7C00CDEC90 /* GTRepository+Pull.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D1BDED1B31FE7C00CDEC90 /* GTRepository+Pull.m */; }; + F8D1BDF11B31FE7C00CDEC90 /* GTRepository+Pull.m in Sources */ = {isa = PBXBuildFile; fileRef = F8D1BDED1B31FE7C00CDEC90 /* GTRepository+Pull.m */; }; + F8D6385C207AC74A00D1FD32 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F8D6385B207AC74A00D1FD32 /* libiconv.tbd */; }; + F8D6385D207AC75100D1FD32 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 23BB67BB1C7DF45300A37A66 /* libz.tbd */; }; + F8D6385F207ACAE600D1FD32 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F8D6385E207ACAE600D1FD32 /* libz.tbd */; }; + F8D63861207ACAF700D1FD32 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F8D63860207ACAF600D1FD32 /* libiconv.tbd */; }; + F8E4A2911A170CA6006485A8 /* GTRemotePushSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F8E4A2901A170CA6006485A8 /* GTRemotePushSpec.m */; }; + F8EFA03A1B4059ED000FF7D0 /* GTUtilityFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = F8EFA0391B4059ED000FF7D0 /* GTUtilityFunctions.m */; }; + F964D5F11CE9D9B200F1D8DD /* GTNote.h in Headers */ = {isa = PBXBuildFile; fileRef = F964D5EF1CE9D9B200F1D8DD /* GTNote.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F964D5F21CE9D9B200F1D8DD /* GTNote.h in Headers */ = {isa = PBXBuildFile; fileRef = F964D5EF1CE9D9B200F1D8DD /* GTNote.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F964D5F31CE9D9B200F1D8DD /* GTNote.m in Sources */ = {isa = PBXBuildFile; fileRef = F964D5F01CE9D9B200F1D8DD /* GTNote.m */; }; + F964D5F51CE9D9B200F1D8DD /* GTNote.m in Sources */ = {isa = PBXBuildFile; fileRef = F964D5F01CE9D9B200F1D8DD /* GTNote.m */; }; + F9D1D4251CEB7BA6009E5855 /* GTNoteSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 6A28265A17C69D6300C6A948 /* PBXContainerItemProxy */ = { + 3D6123BD1A6432F6008F831A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; - remoteGlobalIDString = 6A28265217C69CB400C6A948; - remoteInfo = "OpenSSL-iOS"; + remoteGlobalIDString = D0A330ED16027F1E00A616FA; + remoteInfo = libgit2; + }; + 4DB970A22645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DAEB6B8E1943873100289F44; + remoteInfo = "Quick-macOS"; + }; + 4DB970A42645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DAEB6B991943873100289F44; + remoteInfo = "Quick - macOSTests"; + }; + 4DB970A62645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = CD89D17B247A673100C1F086; + remoteInfo = QuickIssue853RegressionTests; + }; + 4DB970A82645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DA5663E81A4C8D8500193C88; + remoteInfo = "QuickFocused - macOSTests"; + }; + 4DB970AA2645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 64076CF51D6D7C2000E2B499; + remoteInfo = "QuickAfterSuite - macOSTests"; + }; + 4DB970AC2645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 5A5D117C19473F2100F6D13D; + remoteInfo = "Quick-iOS"; + }; + 4DB970AE2645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 5A5D118619473F2100F6D13D; + remoteInfo = "Quick - iOSTests"; + }; + 4DB970B02645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DA9876B21A4C70EB0004AA17; + remoteInfo = "QuickFocused - iOSTests"; + }; + 4DB970B22645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 64076D081D6D7CD600E2B499; + remoteInfo = "QuickAfterSuite - iOSTests"; + }; + 4DB970B42645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F118CD51BDCA4AB005013A2; + remoteInfo = "Quick-tvOS"; + }; + 4DB970B62645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F118CDE1BDCA4AB005013A2; + remoteInfo = "Quick - tvOSTests"; + }; + 4DB970B82645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F118CF01BDCA4BB005013A2; + remoteInfo = "QuickFocused - tvOSTests"; + }; + 4DB970BA2645C8BB00D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 64076D1A1D6D7CEA00E2B499; + remoteInfo = "QuickAfterSuite - tvOSTests"; + }; + 4DB971012645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F925EAD195C0D6300ED456B; + remoteInfo = "Nimble-macOS"; + }; + 4DB971032645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F925EB7195C0D6300ED456B; + remoteInfo = "Nimble-macOSTests"; + }; + 4DB971052645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F1A74291940169200FFFC47; + remoteInfo = "Nimble-iOS"; + }; + 4DB971072645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F1A74341940169200FFFC47; + remoteInfo = "Nimble-iOSTests"; + }; + 4DB971092645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F5DF1551BDCA0CE00C3A531; + remoteInfo = "Nimble-tvOS"; + }; + 4DB9710B2645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1F5DF15E1BDCA0CE00C3A531; + remoteInfo = "Nimble-tvOSTests"; + }; + 4DB971142645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B423AE1A1C0DF76A0004A2F1; + remoteInfo = "ZipArchive-iOS"; + }; + 4DB971162645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = AFF75A241C37279600F450AC; + remoteInfo = "ZipArchive-Mac"; + }; + 4DB971182645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 37952C261F63B50D00DD6677; + remoteInfo = "ZipArchive-tvos"; + }; + 4DB9711A2645C8F300D14944 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 37952C5E1F63BB7100DD6677; + remoteInfo = "ZipArchive-watchos"; + }; + 4DD769842645D15D007599B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = DAEB6B8D1943873100289F44; + remoteInfo = "Quick-macOS"; + }; + 4DD769862645D15D007599B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 1F925EAC195C0D6300ED456B; + remoteInfo = "Nimble-macOS"; + }; + 4DD769882645D15D007599B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = AFF75A231C37279600F450AC; + remoteInfo = "ZipArchive-Mac"; + }; + 4DD769A32645D16B007599B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 5A5D117B19473F2100F6D13D; + remoteInfo = "Quick-iOS"; + }; + 4DD769A52645D16B007599B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 1F1A74281940169200FFFC47; + remoteInfo = "Nimble-iOS"; + }; + 4DD769A72645D16B007599B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = B423AE191C0DF76A0004A2F1; + remoteInfo = "ZipArchive-iOS"; }; 6A3C609C17D5964E00382DFF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -332,19 +612,19 @@ remoteGlobalIDString = 8DC2EF4F0486A6940098B216; remoteInfo = ObjectiveGit; }; - D0A330F716027F4900A616FA /* PBXContainerItemProxy */ = { + D019779619F8335100F523DA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; - remoteGlobalIDString = D0A330ED16027F1E00A616FA; - remoteInfo = libgit2; + remoteGlobalIDString = D0A330F216027F3600A616FA; + remoteInfo = "libgit2-iOS"; }; - D0A330F916027F4D00A616FA /* PBXContainerItemProxy */ = { + F879D83D1B4B7F7D002D5C07 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; proxyType = 1; - remoteGlobalIDString = D0A330F216027F3600A616FA; - remoteInfo = "libgit2-iOS"; + remoteGlobalIDString = D01B6ED219F82E2000D411BC; + remoteInfo = "ObjectiveGit-iOS"; }; /* End PBXContainerItemProxy section */ @@ -360,20 +640,28 @@ name = "Copy git2 Headers"; runOnlyForDeploymentPostprocessing = 0; }; + BEF7E4DF1A3A47450035BB8E /* Copy git2 Headers again */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Headers/git2/sys; + dstSubfolderSpec = 1; + files = ( + BEF7E4E01A3A47690035BB8E /* git2 in Copy git2 Headers again */, + ); + name = "Copy git2 Headers again"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 04DB4645133AB57600D9C624 /* libObjectiveGit-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libObjectiveGit-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 04DB4671133AB5FE00D9C624 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; - 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; - 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - 188DC01817FC1571007350CD /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; 200578C418932A82001C06C3 /* GTBlameSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTBlameSpec.m; sourceTree = ""; }; 2089E43B17D9A58000F451DA /* GTTagSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTTagSpec.m; sourceTree = ""; }; 20F43DE118A2F667007D3621 /* GTRepository+Blame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Blame.h"; sourceTree = ""; }; 20F43DE218A2F667007D3621 /* GTRepository+Blame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Blame.m"; sourceTree = ""; }; + 23BB67BB1C7DF45300A37A66 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 23F39FAB1C86DB1C00849F3C /* GTRepository+Merging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Merging.h"; sourceTree = ""; }; + 23F39FAC1C86DB1C00849F3C /* GTRepository+Merging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Merging.m"; sourceTree = ""; }; 3011D8691668E48500CE3409 /* GTDiffFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiffFile.h; sourceTree = ""; }; 3011D86A1668E48500CE3409 /* GTDiffFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffFile.m; sourceTree = ""; }; 3011D86F1668E78500CE3409 /* GTDiffHunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiffHunk.h; sourceTree = ""; }; @@ -399,15 +687,28 @@ 30DCBA7017B4791A009B0EBD /* NSArray+StringArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+StringArray.m"; sourceTree = ""; }; 30FDC07D16835A8100654BF0 /* GTDiffLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiffLine.h; sourceTree = ""; }; 30FDC07E16835A8100654BF0 /* GTDiffLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffLine.m; sourceTree = ""; }; - 32DBCF5E0370ADEE00C91783 /* ObjectiveGitFramework_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectiveGitFramework_Prefix.pch; sourceTree = ""; }; - 4D103ADC1819CFAA0029DB24 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; }; 4D12323F178E009E0048F785 /* GTRepositoryCommittingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRepositoryCommittingSpec.m; sourceTree = ""; }; 4D1C40D7182C006D00BE2960 /* GTBlobSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTBlobSpec.m; sourceTree = ""; }; 4D79C0EC17DF9F4D00997DE4 /* GTCredential.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTCredential.h; sourceTree = ""; }; 4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTCredential.m; sourceTree = ""; }; 4D79C0F617DFAA7100997DE4 /* GTCredential+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTCredential+Private.h"; sourceTree = ""; }; - 4D8DADBE181A7D9F001B1202 /* libgit2 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = libgit2; path = External/libgit2; sourceTree = ""; }; + 4D9BCD23206D84AD003CD3CE /* libgit2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgit2.a; path = External/build/lib/libgit2.a; sourceTree = ""; }; + 4DB97074264596AE00D14944 /* macOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-Application.xcconfig"; sourceTree = ""; }; + 4DB97075264596AE00D14944 /* macOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-Framework.xcconfig"; sourceTree = ""; }; + 4DB97076264596AE00D14944 /* macOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-StaticLibrary.xcconfig"; sourceTree = ""; }; + 4DB97077264596AE00D14944 /* macOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-Base.xcconfig"; sourceTree = ""; }; + 4DB97078264596AE00D14944 /* macOS-XCTest.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-XCTest.xcconfig"; sourceTree = ""; }; + 4DB97079264596AE00D14944 /* macOS-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "macOS-DynamicLibrary.xcconfig"; sourceTree = ""; }; + 4DB970912645C8BA00D14944 /* Quick.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Quick.xcodeproj; path = Carthage/Checkouts/Quick/Quick.xcodeproj; sourceTree = ""; }; + 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Nimble.xcodeproj; path = Carthage/Checkouts/Nimble/Nimble.xcodeproj; sourceTree = ""; }; + 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ZipArchive.xcodeproj; path = Carthage/Checkouts/ZipArchive/ZipArchive.xcodeproj; sourceTree = ""; }; + 4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRemoteSpec.m; sourceTree = ""; }; + 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTCheckoutOptions.h; sourceTree = ""; }; + 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTCheckoutOptions.m; sourceTree = ""; }; + 4DD7422B25D3F8CB009D9A17 /* libgit2-mac.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libgit2-mac.a"; path = "External/libgit2-mac.a"; sourceTree = ""; }; 4DE864341794A37E00371A65 /* GTRepository+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Private.h"; sourceTree = ""; }; + 4DFFB159183AA8D600D1565E /* GTRepository+RemoteOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+RemoteOperations.h"; sourceTree = ""; }; + 4DFFB15A183AA8D600D1565E /* GTRepository+RemoteOperations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+RemoteOperations.m"; sourceTree = ""; }; 55C8054C13861F34004DCB0F /* GTObjectDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTObjectDatabase.h; sourceTree = ""; }; 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GTObjectDatabase.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 55C8057213874CDF004DCB0F /* NSString+Git.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Git.h"; sourceTree = ""; }; @@ -415,13 +716,13 @@ 5BE612861745EE3300266D8C /* GTTreeBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTTreeBuilder.h; sourceTree = ""; }; 5BE612871745EE3300266D8C /* GTTreeBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTTreeBuilder.m; sourceTree = ""; }; 5BE612921745EEBC00266D8C /* GTTreeBuilderSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTTreeBuilderSpec.m; sourceTree = ""; }; - 6A1F2FD317C6A8F3003DFADE /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcrypto.a; path = "External/ios-openssl/lib/libcrypto.a"; sourceTree = ""; }; - 6A1F2FD417C6A8F3003DFADE /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssl.a; path = "External/ios-openssl/lib/libssl.a"; sourceTree = ""; }; - 6A3C60A017D5987600382DFF /* update_libssh2_ios */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = update_libssh2_ios; path = script/update_libssh2_ios; sourceTree = SOURCE_ROOT; }; - 6A502B8617D6892D00BAF4A5 /* libssh2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssh2.a; path = "External/libssh2-ios/lib/libssh2.a"; sourceTree = ""; }; + 6A1F2FD317C6A8F3003DFADE /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcrypto.a; path = External/build/lib/libcrypto.a; sourceTree = ""; }; + 6A1F2FD417C6A8F3003DFADE /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssl.a; path = External/build/lib/libssl.a; sourceTree = ""; }; + 6A502B8617D6892D00BAF4A5 /* libssh2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssh2.a; path = External/build/lib/libssh2.a; sourceTree = ""; }; + 6EEB519F199D62B9001D72C0 /* GTFetchHeadEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTFetchHeadEntry.h; sourceTree = ""; }; + 6EEB51A0199D62B9001D72C0 /* GTFetchHeadEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTFetchHeadEntry.m; sourceTree = ""; }; 79262F0E13C697BE00A4B1EA /* git2 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = git2; path = External/libgit2/include/git2; sourceTree = ""; }; 79262F8A13C69B1600A4B1EA /* git2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = git2.h; path = External/libgit2/include/git2.h; sourceTree = ""; }; - 8803DA861313145700E6E818 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; 880EE65F18AE700500B82455 /* GTFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTFilter.h; sourceTree = ""; }; 880EE66018AE700500B82455 /* GTFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTFilter.m; sourceTree = ""; }; 882154671714740500D76B76 /* GTReflog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTReflog.h; sourceTree = ""; }; @@ -442,30 +743,33 @@ 886E622818AEBF75000611A0 /* GTFilterSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTFilterSource.h; sourceTree = ""; }; 886E622918AEBF75000611A0 /* GTFilterSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTFilterSource.m; sourceTree = ""; }; 886E623618AECD86000611A0 /* GTFilterSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTFilterSpec.m; sourceTree = ""; }; + 8870390A1975E3F2004118D7 /* GTDiffDeltaSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffDeltaSpec.m; sourceTree = ""; }; 88746CC217FA1C950005888A /* GTRepository+Committing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Committing.h"; sourceTree = ""; }; 88746CC317FA1C950005888A /* GTRepository+Committing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Committing.m"; sourceTree = ""; }; + 887B948D1A3A38130070D41D /* ObjectiveGit.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = ObjectiveGit.modulemap; sourceTree = ""; }; 887DAFF615CB1C8000F30D0D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 88948AC81779243600809CDA /* GTObjectDatabaseSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTObjectDatabaseSpec.m; sourceTree = ""; }; + 889923F919FF5DD40092A9A6 /* git2 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = git2; path = External/libgit2/include/git2; sourceTree = ""; }; 88A994B916FCE7D400402C7B /* GTBranchSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTBranchSpec.m; sourceTree = ""; }; - 88A994C916FCED1D00402C7B /* GTTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTTestCase.h; sourceTree = ""; }; - 88A994CA16FCED1D00402C7B /* GTTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTTestCase.m; sourceTree = ""; }; + 88A994C916FCED1D00402C7B /* QuickSpec+GTFixtures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "QuickSpec+GTFixtures.h"; sourceTree = ""; }; + 88A994CA16FCED1D00402C7B /* QuickSpec+GTFixtures.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "QuickSpec+GTFixtures.m"; sourceTree = ""; }; + 88B2131A1B20E785005CF2C5 /* GTRepository+References.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+References.h"; sourceTree = ""; }; + 88B2131B1B20E785005CF2C5 /* GTRepository+References.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+References.m"; sourceTree = ""; }; 88BC0E4E18EF4F3600C7D0E6 /* GTRepository+Reset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Reset.h"; sourceTree = ""; }; 88BC0E4F18EF4F3600C7D0E6 /* GTRepository+Reset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Reset.m"; sourceTree = ""; }; 88C0BC5817038CF3009E99AA /* GTConfigurationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTConfigurationSpec.m; sourceTree = ""; }; + 88E352FE1982E9160051001F /* GTRepository+Attributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Attributes.h"; sourceTree = ""; }; + 88E352FF1982E9160051001F /* GTRepository+Attributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Attributes.m"; sourceTree = ""; }; + 88E353051982EA6B0051001F /* GTRepositoryAttributesSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRepositoryAttributesSpec.m; sourceTree = ""; }; 88EB7E4B14AEBA600046FEA4 /* GTConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTConfiguration.h; sourceTree = ""; }; 88EB7E4C14AEBA600046FEA4 /* GTConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTConfiguration.m; sourceTree = ""; }; - 88F05A6B16011E5400B7AD1D /* ObjectiveGitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ObjectiveGitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; - 88F05A7716011E5400B7AD1D /* ObjectiveGitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ObjectiveGitTests-Info.plist"; sourceTree = ""; }; - 88F05A7916011E5400B7AD1D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 88F05A7E16011E5400B7AD1D /* ObjectiveGitTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ObjectiveGitTests-Prefix.pch"; sourceTree = ""; }; - 88F05A9C16011F6A00B7AD1D /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; + 88F05A6B16011E5400B7AD1D /* ObjectiveGit-MacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ObjectiveGit-MacTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 88F05AA416011FFD00B7AD1D /* GTCommitSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTCommitSpec.m; sourceTree = ""; }; - 88F05AA816011FFD00B7AD1D /* GTObjectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTObjectTest.m; sourceTree = ""; }; - 88F05AAF16011FFD00B7AD1D /* ObjectiveGitTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "ObjectiveGitTests-Info.plist"; sourceTree = ""; }; - 88F05AC51601209A00B7AD1D /* ObjectiveGit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ObjectiveGit.m; path = Classes/ObjectiveGit.m; sourceTree = ""; }; + 88F05AA816011FFD00B7AD1D /* GTObjectSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTObjectSpec.m; sourceTree = ""; }; + 88F05AC51601209A00B7AD1D /* ObjectiveGit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectiveGit.m; sourceTree = ""; }; 88F50F56132054D800584FBE /* GTBranch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTBranch.h; sourceTree = ""; }; 88F50F57132054D800584FBE /* GTBranch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GTBranch.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 88F6D9D81320451F00CC0BA8 /* ObjectiveGit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = ObjectiveGit.h; path = Classes/ObjectiveGit.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 88F6D9D81320451F00CC0BA8 /* ObjectiveGit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ObjectiveGit.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* ObjectiveGit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ObjectiveGit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AA046110134F4D2000DF526B /* GTOdbObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTOdbObject.h; sourceTree = ""; }; @@ -491,9 +795,6 @@ BDD8AB01130F01AB00CB5D40 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; }; BDD8AE6D13131B8800CB5D40 /* GTEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTEnumerator.h; sourceTree = ""; }; BDD8AE6E13131B8800CB5D40 /* GTEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GTEnumerator.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BDD9C3FB133BA604003708E7 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - BDD9C3FD133BA604003708E7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - BDD9C3FF133BA604003708E7 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; BDE4C060130EFE2C00851650 /* NSError+Git.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+Git.h"; sourceTree = ""; }; BDE4C061130EFE2C00851650 /* NSError+Git.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+Git.m"; sourceTree = ""; }; BDE4C062130EFE2C00851650 /* GTRepository.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTRepository.h; sourceTree = ""; }; @@ -506,6 +807,12 @@ D015F7C817F695E800AD5E1F /* GTRepository+Stashing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Stashing.h"; sourceTree = ""; }; D015F7C917F695E800AD5E1F /* GTRepository+Stashing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Stashing.m"; sourceTree = ""; }; D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRepositoryStashingSpec.m; sourceTree = ""; }; + D019778B19F830CC00F523DA /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; + D019778C19F830D100F523DA /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Framework.xcconfig"; sourceTree = ""; }; + D01B6ED319F82E2000D411BC /* ObjectiveGit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ObjectiveGit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D01B6F0F19F82F3C00D411BC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D01EFD9F195DEF2200838D24 /* NSDataGitSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSDataGitSpec.m; sourceTree = ""; }; + D01EFDB6195E021800838D24 /* GTDiff+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GTDiff+Private.h"; sourceTree = ""; }; D03B579F18BFFF07007124F4 /* GTDiffPatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTDiffPatch.h; sourceTree = ""; }; D03B57A018BFFF07007124F4 /* GTDiffPatch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTDiffPatch.m; sourceTree = ""; }; D03B7C401756AB370034A610 /* GTSubmoduleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTSubmoduleSpec.m; sourceTree = ""; }; @@ -516,6 +823,7 @@ D09C2E341755F16200065E36 /* GTSubmodule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTSubmodule.h; sourceTree = ""; }; D09C2E351755F16200065E36 /* GTSubmodule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTSubmodule.m; sourceTree = ""; }; D09C2E50175602A500065E36 /* fixtures.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; name = fixtures.zip; path = fixtures/fixtures.zip; sourceTree = ""; }; + D0A0129619F9A660007F1914 /* SwiftSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSpec.swift; sourceTree = ""; }; D0A463D617E57C45000F5021 /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; D0A463D817E57C45000F5021 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; D0A463D917E57C45000F5021 /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; @@ -529,54 +837,79 @@ D0D81863174421EB00995A2E /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; D0D81864174421EB00995A2E /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; D0D81865174421EB00995A2E /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; - D0D81867174421EB00995A2E /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; - D0D81868174421EB00995A2E /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; - D0D81869174421EB00995A2E /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; - D0D8186A174421EB00995A2E /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; - D0D8186B174421EB00995A2E /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; D0D8186C174421EB00995A2E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = ""; }; D0F4E28917C7F24200BBDE30 /* NSErrorGitSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSErrorGitSpec.m; sourceTree = ""; }; - D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; - D4ACA56018DBA88E00EBD899 /* libSpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSpecta.a; path = specta/build/Debug/libSpecta.a; sourceTree = ""; }; - D4ACA56218DBA89300EBD899 /* libExpecta.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libExpecta.a; path = expecta/build/Debug/libExpecta.a; sourceTree = ""; }; DD3D9510182A81E1004AF532 /* GTBlame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTBlame.h; sourceTree = ""; }; DD3D9511182A81E1004AF532 /* GTBlame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTBlame.m; sourceTree = ""; }; DD3D951A182AB25C004AF532 /* GTBlameHunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTBlameHunk.h; sourceTree = ""; }; DD3D951B182AB25C004AF532 /* GTBlameHunk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTBlameHunk.m; sourceTree = ""; }; - E46931A7172740D300F2077D /* update_libgit2 */ = {isa = PBXFileReference; lastKnownFileType = text; name = update_libgit2; path = script/update_libgit2; sourceTree = ""; }; - E46931A8172740D300F2077D /* update_libgit2_ios */ = {isa = PBXFileReference; lastKnownFileType = text; name = update_libgit2_ios; path = script/update_libgit2_ios; sourceTree = ""; }; - F6ED8DA0180E713200A32D40 /* GTRemoteSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRemoteSpec.m; sourceTree = ""; }; + F81B6B51207B0A7700AB0836 /* Extension.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Extension.xcconfig; sourceTree = ""; }; + F81B6B53207B0A8B00AB0836 /* iOS-Extension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Extension.xcconfig"; sourceTree = ""; }; + F879D82F1B4B77F4002D5C07 /* Libgit2FeaturesSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Libgit2FeaturesSpec.m; sourceTree = ""; }; + F879D8361B4B7F7C002D5C07 /* ObjectiveGit-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ObjectiveGit-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + F8D1BDEC1B31FE7C00CDEC90 /* GTRepository+Pull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Pull.h"; sourceTree = ""; }; + F8D1BDED1B31FE7C00CDEC90 /* GTRepository+Pull.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Pull.m"; sourceTree = ""; }; + F8D6384A207A618D00D1FD32 /* script */ = {isa = PBXFileReference; lastKnownFileType = folder; path = script; sourceTree = ""; }; + F8D6385B207AC74A00D1FD32 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; + F8D6385E207ACAE600D1FD32 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; }; + F8D63860207ACAF600D1FD32 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/usr/lib/libiconv.tbd; sourceTree = DEVELOPER_DIR; }; + F8D63871207AE62A00D1FD32 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + F8D63872207AE63A00D1FD32 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + F8E4A2901A170CA6006485A8 /* GTRemotePushSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRemotePushSpec.m; sourceTree = ""; }; + F8EFA0361B405020000FF7D0 /* GTRepository+PullSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+PullSpec.m"; sourceTree = ""; }; + F8EFA0381B4059ED000FF7D0 /* GTUtilityFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTUtilityFunctions.h; sourceTree = ""; }; + F8EFA0391B4059ED000FF7D0 /* GTUtilityFunctions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTUtilityFunctions.m; sourceTree = ""; }; + F964D5EF1CE9D9B200F1D8DD /* GTNote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTNote.h; sourceTree = ""; }; + F964D5F01CE9D9B200F1D8DD /* GTNote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTNote.m; sourceTree = ""; }; + F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTNoteSpec.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 04DB4643133AB57600D9C624 /* Frameworks */ = { + 88F05A6716011E5400B7AD1D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 188DC01917FC1571007350CD /* libz.dylib in Frameworks */, - 6A1F2FD517C6A8F3003DFADE /* libcrypto.a in Frameworks */, - 6A1F2FD617C6A8F3003DFADE /* libssl.a in Frameworks */, + F81B6B50207B032800AB0836 /* XCTest.framework in Frameworks */, + 88F05A9E16011F6E00B7AD1D /* ObjectiveGit.framework in Frameworks */, + 4DB9711D2645CA3700D14944 /* Quick.framework in Frameworks */, + 4DD769A92645D214007599B8 /* Nimble.framework in Frameworks */, + 4DB9711F2645CA5200D14944 /* ZipArchive.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 88F05A6716011E5400B7AD1D /* Frameworks */ = { + 8DC2EF560486A6940098B216 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 88F05A9E16011F6E00B7AD1D /* ObjectiveGit.framework in Frameworks */, - 88F05A9D16011F6A00B7AD1D /* SenTestingKit.framework in Frameworks */, - D4ACA56118DBA88E00EBD899 /* libSpecta.a in Frameworks */, - D4ACA56318DBA89300EBD899 /* libExpecta.a in Frameworks */, + F8D6385D207AC75100D1FD32 /* libz.tbd in Frameworks */, + F8D6385C207AC74A00D1FD32 /* libiconv.tbd in Frameworks */, + 4DD7422C25D3F8CB009D9A17 /* libgit2-mac.a in Frameworks */, + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 8DC2EF560486A6940098B216 /* Frameworks */ = { + D01B6ECF19F82E2000D411BC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4D103ADD1819CFAA0029DB24 /* libiconv.dylib in Frameworks */, - 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, - 8803DA871313145700E6E818 /* libz.dylib in Frameworks */, + F8D63861207ACAF700D1FD32 /* libiconv.tbd in Frameworks */, + F8D6385F207ACAE600D1FD32 /* libz.tbd in Frameworks */, + D05FC5E319FAD03C008C9348 /* libssl.a in Frameworks */, + D05FC5E219FAD039008C9348 /* libcrypto.a in Frameworks */, + D05FC5E419FAD040008C9348 /* libssh2.a in Frameworks */, + 4D9BCD25206D84B2003CD3CE /* libgit2.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F879D8331B4B7F7C002D5C07 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F84277BE207B104A008AB8E8 /* XCTest.framework in Frameworks */, + F879D83C1B4B7F7D002D5C07 /* ObjectiveGit.framework in Frameworks */, + 4DB971202645CA5D00D14944 /* Quick.framework in Frameworks */, + 4DB971212645CA6600D14944 /* Nimble.framework in Frameworks */, + 4DB971222645CA6D00D14944 /* ZipArchive.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -587,8 +920,9 @@ isa = PBXGroup; children = ( 8DC2EF5B0486A6940098B216 /* ObjectiveGit.framework */, - 04DB4645133AB57600D9C624 /* libObjectiveGit-iOS.a */, - 88F05A6B16011E5400B7AD1D /* ObjectiveGitTests.octest */, + 88F05A6B16011E5400B7AD1D /* ObjectiveGit-MacTests.xctest */, + D01B6ED319F82E2000D411BC /* ObjectiveGit.framework */, + F879D8361B4B7F7C002D5C07 /* ObjectiveGit-iOSTests.xctest */, ); name = Products; sourceTree = ""; @@ -597,16 +931,16 @@ isa = PBXGroup; children = ( BDD8AB01130F01AB00CB5D40 /* README.md */, - 88F6D9D81320451F00CC0BA8 /* ObjectiveGit.h */, - 88F05AC51601209A00B7AD1D /* ObjectiveGit.m */, - BDE4C05E130EFE2C00851650 /* Classes */, + 887B948D1A3A38130070D41D /* ObjectiveGit.modulemap */, + BDE4C05E130EFE2C00851650 /* ObjectiveGit */, 88F05A7516011E5400B7AD1D /* ObjectiveGitTests */, 32C88DFF0371C24200C91783 /* Other Sources */, 089C1665FE841158C02AAC07 /* Resources */, 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, D0D81857174421EB00995A2E /* Configuration */, 034768DFFF38A50411DB9C8B /* Products */, - E46931A6172740D300F2077D /* Scripts */, + F8D6384A207A618D00D1FD32 /* script */, + 4DB9711C2645CA3700D14944 /* Frameworks */, ); name = ObjectiveGitFramework; sourceTree = ""; @@ -615,10 +949,22 @@ 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = { isa = PBXGroup; children = ( - 4D8DADBE181A7D9F001B1202 /* libgit2 */, + 4DB970902645C8A500D14944 /* Carthage */, 306123A817EA5261006591D4 /* extobjc */, - 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */, - 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */, + 889923F919FF5DD40092A9A6 /* git2 */, + 6A1F2FD317C6A8F3003DFADE /* libcrypto.a */, + F8D63860207ACAF600D1FD32 /* libiconv.tbd */, + F8D6385B207AC74A00D1FD32 /* libiconv.tbd */, + 23BB67BB1C7DF45300A37A66 /* libz.tbd */, + F8D6385E207ACAE600D1FD32 /* libz.tbd */, + 6A1F2FD417C6A8F3003DFADE /* libssl.a */, + 6A502B8617D6892D00BAF4A5 /* libssh2.a */, + 4D9BCD23206D84AD003CD3CE /* libgit2.a */, + 4DD7422B25D3F8CB009D9A17 /* libgit2-mac.a */, + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, + 887DAFF615CB1C8000F30D0D /* Security.framework */, + F8D63871207AE62A00D1FD32 /* XCTest.framework */, + F8D63872207AE63A00D1FD32 /* XCTest.framework */, ); name = "External Frameworks and Libraries"; sourceTree = ""; @@ -627,40 +973,10 @@ isa = PBXGroup; children = ( 8DC2EF5A0486A6940098B216 /* Info.plist */, - 089C1666FE841158C02AAC07 /* InfoPlist.strings */, ); name = Resources; sourceTree = ""; }; - 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = { - isa = PBXGroup; - children = ( - 4D103ADC1819CFAA0029DB24 /* libiconv.dylib */, - 188DC01817FC1571007350CD /* libz.dylib */, - 8803DA861313145700E6E818 /* libz.dylib */, - 04DB4671133AB5FE00D9C624 /* libz.dylib */, - 6A1F2FD317C6A8F3003DFADE /* libcrypto.a */, - 6A1F2FD417C6A8F3003DFADE /* libssl.a */, - 6A502B8617D6892D00BAF4A5 /* libssh2.a */, - 887DAFF615CB1C8000F30D0D /* Security.framework */, - 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, - ); - name = "Linked Frameworks"; - sourceTree = ""; - }; - 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = { - isa = PBXGroup; - children = ( - 0867D6A5FE840307C02AAC07 /* AppKit.framework */, - D2F7E79907B2D74100F64583 /* CoreData.framework */, - BDD9C3FB133BA604003708E7 /* CoreGraphics.framework */, - 0867D69BFE84028FC02AAC07 /* Foundation.framework */, - BDD9C3FD133BA604003708E7 /* Foundation.framework */, - BDD9C3FF133BA604003708E7 /* UIKit.framework */, - ); - name = "Other Frameworks"; - sourceTree = ""; - }; 306123A817EA5261006591D4 /* extobjc */ = { isa = PBXGroup; children = ( @@ -676,6 +992,7 @@ isa = PBXGroup; children = ( 30A3D6521667F11C00C49A39 /* GTDiff.h */, + D01EFDB6195E021800838D24 /* GTDiff+Private.h */, 30A3D6531667F11C00C49A39 /* GTDiff.m */, 3011D8691668E48500CE3409 /* GTDiffFile.h */, 3011D86A1668E48500CE3409 /* GTDiffFile.m */, @@ -694,46 +1011,126 @@ 32C88DFF0371C24200C91783 /* Other Sources */ = { isa = PBXGroup; children = ( - 32DBCF5E0370ADEE00C91783 /* ObjectiveGitFramework_Prefix.pch */, 79262F0E13C697BE00A4B1EA /* git2 */, 79262F8A13C69B1600A4B1EA /* git2.h */, ); name = "Other Sources"; sourceTree = ""; }; + 4DB97073264596AE00D14944 /* macOS */ = { + isa = PBXGroup; + children = ( + 4DB97074264596AE00D14944 /* macOS-Application.xcconfig */, + 4DB97075264596AE00D14944 /* macOS-Framework.xcconfig */, + 4DB97076264596AE00D14944 /* macOS-StaticLibrary.xcconfig */, + 4DB97077264596AE00D14944 /* macOS-Base.xcconfig */, + 4DB97078264596AE00D14944 /* macOS-XCTest.xcconfig */, + 4DB97079264596AE00D14944 /* macOS-DynamicLibrary.xcconfig */, + ); + path = macOS; + sourceTree = ""; + }; + 4DB970902645C8A500D14944 /* Carthage */ = { + isa = PBXGroup; + children = ( + 4DB970912645C8BA00D14944 /* Quick.xcodeproj */, + 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */, + 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */, + ); + name = Carthage; + sourceTree = ""; + }; + 4DB970922645C8BA00D14944 /* Products */ = { + isa = PBXGroup; + children = ( + 4DB970A32645C8BB00D14944 /* Quick.framework */, + 4DB970A52645C8BB00D14944 /* Quick - macOSTests.xctest */, + 4DB970A72645C8BB00D14944 /* QuickIssue853RegressionTests.xctest */, + 4DB970A92645C8BB00D14944 /* QuickFocused - macOSTests.xctest */, + 4DB970AB2645C8BB00D14944 /* QuickAfterSuite - macOSTests.xctest */, + 4DB970AD2645C8BB00D14944 /* Quick.framework */, + 4DB970AF2645C8BB00D14944 /* Quick - iOSTests.xctest */, + 4DB970B12645C8BB00D14944 /* QuickFocused - iOSTests.xctest */, + 4DB970B32645C8BB00D14944 /* QuickAfterSuite - iOSTests.xctest */, + 4DB970B52645C8BB00D14944 /* Quick.framework */, + 4DB970B72645C8BB00D14944 /* Quick - tvOSTests.xctest */, + 4DB970B92645C8BB00D14944 /* QuickFocused - tvOSTests.xctest */, + 4DB970BB2645C8BB00D14944 /* QuickAfterSuite - tvOSTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 4DB970F82645C8F300D14944 /* Products */ = { + isa = PBXGroup; + children = ( + 4DB971022645C8F300D14944 /* Nimble.framework */, + 4DB971042645C8F300D14944 /* NimbleTests.xctest */, + 4DB971062645C8F300D14944 /* Nimble.framework */, + 4DB971082645C8F300D14944 /* NimbleTests.xctest */, + 4DB9710A2645C8F300D14944 /* Nimble.framework */, + 4DB9710C2645C8F300D14944 /* NimbleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 4DB9710E2645C8F300D14944 /* Products */ = { + isa = PBXGroup; + children = ( + 4DB971152645C8F300D14944 /* ZipArchive.framework */, + 4DB971172645C8F300D14944 /* ZipArchive.framework */, + 4DB971192645C8F300D14944 /* ZipArchive.framework */, + 4DB9711B2645C8F300D14944 /* ZipArchive.framework */, + ); + name = Products; + sourceTree = ""; + }; + 4DB9711C2645CA3700D14944 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 88F05A7516011E5400B7AD1D /* ObjectiveGitTests */ = { isa = PBXGroup; children = ( - 88F05A7616011E5400B7AD1D /* Supporting Files */, + 200578C418932A82001C06C3 /* GTBlameSpec.m */, 4D1C40D7182C006D00BE2960 /* GTBlobSpec.m */, 88A994B916FCE7D400402C7B /* GTBranchSpec.m */, 88F05AA416011FFD00B7AD1D /* GTCommitSpec.m */, 88C0BC5817038CF3009E99AA /* GTConfigurationSpec.m */, + 8870390A1975E3F2004118D7 /* GTDiffDeltaSpec.m */, 30865A90167F503400B1AB6E /* GTDiffSpec.m */, D06D9E001755D10000558C17 /* GTEnumeratorSpec.m */, + D0751CD818BE520400134314 /* GTFilterListSpec.m */, + 886E623618AECD86000611A0 /* GTFilterSpec.m */, 8832811E173D8816006D7DCF /* GTIndexSpec.m */, - 88F05AA816011FFD00B7AD1D /* GTObjectTest.m */, + F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */, + 88948AC81779243600809CDA /* GTObjectDatabaseSpec.m */, + 88F05AA816011FFD00B7AD1D /* GTObjectSpec.m */, + D040AF6F177B9779001AD9EB /* GTOIDSpec.m */, D00F6815175D373C004DB9D6 /* GTReferenceSpec.m */, 88215482171499BE00D76B76 /* GTReflogSpec.m */, - 200578C418932A82001C06C3 /* GTBlameSpec.m */, - D0AC906B172F941F00347DC4 /* GTRepositorySpec.m */, - F6ED8DA0180E713200A32D40 /* GTRemoteSpec.m */, + F8E4A2901A170CA6006485A8 /* GTRemotePushSpec.m */, + 4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */, + F8EFA0361B405020000FF7D0 /* GTRepository+PullSpec.m */, + 30A269AC17B4878C000FE64E /* GTRepository+StatusSpec.m */, + 88E353051982EA6B0051001F /* GTRepositoryAttributesSpec.m */, 4D12323F178E009E0048F785 /* GTRepositoryCommittingSpec.m */, + 88234B2518F2FE260039972E /* GTRepositoryResetSpec.m */, + D0AC906B172F941F00347DC4 /* GTRepositorySpec.m */, + D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */, + D040AF77177B9A9E001AD9EB /* GTSignatureSpec.m */, D03B7C401756AB370034A610 /* GTSubmoduleSpec.m */, 2089E43B17D9A58000F451DA /* GTTagSpec.m */, 30B1E7FF1703871900D0814D /* GTTimeAdditionsSpec.m */, 5BE612921745EEBC00266D8C /* GTTreeBuilderSpec.m */, 88328127173D8A64006D7DCF /* GTTreeSpec.m */, - 88948AC81779243600809CDA /* GTObjectDatabaseSpec.m */, - D040AF6F177B9779001AD9EB /* GTOIDSpec.m */, - D040AF77177B9A9E001AD9EB /* GTSignatureSpec.m */, - 30A269AC17B4878C000FE64E /* GTRepository+StatusSpec.m */, + F879D82F1B4B77F4002D5C07 /* Libgit2FeaturesSpec.m */, 307623AA17C6C8BD00E2CDF1 /* NSArray+StringArraySpec.m */, + D01EFD9F195DEF2200838D24 /* NSDataGitSpec.m */, D0F4E28917C7F24200BBDE30 /* NSErrorGitSpec.m */, - D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */, - 886E623618AECD86000611A0 /* GTFilterSpec.m */, - D0751CD818BE520400134314 /* GTFilterListSpec.m */, - 88234B2518F2FE260039972E /* GTRepositoryResetSpec.m */, + 88F05A7616011E5400B7AD1D /* Supporting Files */, ); path = ObjectiveGitTests; sourceTree = ""; @@ -741,23 +1138,22 @@ 88F05A7616011E5400B7AD1D /* Supporting Files */ = { isa = PBXGroup; children = ( - 88A994C916FCED1D00402C7B /* GTTestCase.h */, - 88A994CA16FCED1D00402C7B /* GTTestCase.m */, D09C2E50175602A500065E36 /* fixtures.zip */, - 88F05A9C16011F6A00B7AD1D /* SenTestingKit.framework */, - D4ACA56218DBA89300EBD899 /* libExpecta.a */, - D4ACA56018DBA88E00EBD899 /* libSpecta.a */, - 88F05A7716011E5400B7AD1D /* ObjectiveGitTests-Info.plist */, - 88F05A7816011E5400B7AD1D /* InfoPlist.strings */, - 88F05A7E16011E5400B7AD1D /* ObjectiveGitTests-Prefix.pch */, - 88F05AAF16011FFD00B7AD1D /* ObjectiveGitTests-Info.plist */, + F8EFA0381B4059ED000FF7D0 /* GTUtilityFunctions.h */, + F8EFA0391B4059ED000FF7D0 /* GTUtilityFunctions.m */, + D01B6F0F19F82F3C00D411BC /* Info.plist */, + 88A994C916FCED1D00402C7B /* QuickSpec+GTFixtures.h */, + 88A994CA16FCED1D00402C7B /* QuickSpec+GTFixtures.m */, + D0A0129619F9A660007F1914 /* SwiftSpec.swift */, ); name = "Supporting Files"; sourceTree = ""; }; - BDE4C05E130EFE2C00851650 /* Classes */ = { + BDE4C05E130EFE2C00851650 /* ObjectiveGit */ = { isa = PBXGroup; children = ( + 88F6D9D81320451F00CC0BA8 /* ObjectiveGit.h */, + 88F05AC51601209A00B7AD1D /* ObjectiveGit.m */, BDE4C05F130EFE2C00851650 /* Categories */, BDE4C062130EFE2C00851650 /* GTRepository.h */, 4DE864341794A37E00371A65 /* GTRepository+Private.h */, @@ -772,6 +1168,14 @@ D015F7C917F695E800AD5E1F /* GTRepository+Stashing.m */, 88746CC217FA1C950005888A /* GTRepository+Committing.h */, 88746CC317FA1C950005888A /* GTRepository+Committing.m */, + F8D1BDEC1B31FE7C00CDEC90 /* GTRepository+Pull.h */, + F8D1BDED1B31FE7C00CDEC90 /* GTRepository+Pull.m */, + 4DFFB159183AA8D600D1565E /* GTRepository+RemoteOperations.h */, + 4DFFB15A183AA8D600D1565E /* GTRepository+RemoteOperations.m */, + 88B2131A1B20E785005CF2C5 /* GTRepository+References.h */, + 88B2131B1B20E785005CF2C5 /* GTRepository+References.m */, + 23F39FAB1C86DB1C00849F3C /* GTRepository+Merging.h */, + 23F39FAC1C86DB1C00849F3C /* GTRepository+Merging.m */, BDD8AE6D13131B8800CB5D40 /* GTEnumerator.h */, BDD8AE6E13131B8800CB5D40 /* GTEnumerator.m */, BD6C22A71314625800992935 /* GTObject.h */, @@ -788,6 +1192,8 @@ BD6B0416131496CC001909D0 /* GTTreeEntry.m */, 5BE612861745EE3300266D8C /* GTTreeBuilder.h */, 5BE612871745EE3300266D8C /* GTTreeBuilder.m */, + F964D5EF1CE9D9B200F1D8DD /* GTNote.h */, + F964D5F01CE9D9B200F1D8DD /* GTNote.m */, BDD62922131C03D600DE34D1 /* GTTag.h */, BDD62923131C03D600DE34D1 /* GTTag.m */, BDFAF9C1131C1845000508BC /* GTIndex.h */, @@ -833,8 +1239,14 @@ 886E622918AEBF75000611A0 /* GTFilterSource.m */, D0CE551E18B6C58F008EB8E0 /* GTFilterList.h */, D0CE551F18B6C58F008EB8E0 /* GTFilterList.m */, - ); - path = Classes; + 88E352FE1982E9160051001F /* GTRepository+Attributes.h */, + 88E352FF1982E9160051001F /* GTRepository+Attributes.m */, + 6EEB519F199D62B9001D72C0 /* GTFetchHeadEntry.h */, + 6EEB51A0199D62B9001D72C0 /* GTFetchHeadEntry.m */, + 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */, + 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */, + ); + path = ObjectiveGit; sourceTree = ""; }; BDE4C05F130EFE2C00851650 /* Categories */ = { @@ -879,6 +1291,8 @@ isa = PBXGroup; children = ( D0A463DD17E57C45000F5021 /* Application.xcconfig */, + F81B6B51207B0A7700AB0836 /* Extension.xcconfig */, + D019778B19F830CC00F523DA /* Framework.xcconfig */, D0A463DE17E57C45000F5021 /* StaticLibrary.xcconfig */, ); path = Targets; @@ -887,13 +1301,13 @@ D0D81857174421EB00995A2E /* Configuration */ = { isa = PBXGroup; children = ( + 4DB97073264596AE00D14944 /* macOS */, D0A463D517E57C45000F5021 /* Base */, D0D81862174421EB00995A2E /* iOS */, - D0D81866174421EB00995A2E /* Mac OS X */, D0D8186C174421EB00995A2E /* README.md */, ); name = Configuration; - path = External/Configuration; + path = Carthage/Checkouts/xcconfigs; sourceTree = ""; }; D0D81862174421EB00995A2E /* iOS */ = { @@ -901,93 +1315,16 @@ children = ( D0D81863174421EB00995A2E /* iOS-Application.xcconfig */, D0D81864174421EB00995A2E /* iOS-Base.xcconfig */, + F81B6B53207B0A8B00AB0836 /* iOS-Extension.xcconfig */, + D019778C19F830D100F523DA /* iOS-Framework.xcconfig */, D0D81865174421EB00995A2E /* iOS-StaticLibrary.xcconfig */, ); path = iOS; sourceTree = ""; }; - D0D81866174421EB00995A2E /* Mac OS X */ = { - isa = PBXGroup; - children = ( - D0D81867174421EB00995A2E /* Mac-Application.xcconfig */, - D0D81868174421EB00995A2E /* Mac-Base.xcconfig */, - D0D81869174421EB00995A2E /* Mac-DynamicLibrary.xcconfig */, - D0D8186A174421EB00995A2E /* Mac-Framework.xcconfig */, - D0D8186B174421EB00995A2E /* Mac-StaticLibrary.xcconfig */, - ); - path = "Mac OS X"; - sourceTree = ""; - }; - E46931A6172740D300F2077D /* Scripts */ = { - isa = PBXGroup; - children = ( - 6A3C60A017D5987600382DFF /* update_libssh2_ios */, - E46931A7172740D300F2077D /* update_libgit2 */, - E46931A8172740D300F2077D /* update_libgit2_ios */, - ); - path = Scripts; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 04DB4641133AB57600D9C624 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 886E622B18AEBF75000611A0 /* GTFilterSource.h in Headers */, - 306123B217EA5261006591D4 /* metamacros.h in Headers */, - 3E0A23E5159E0FDB00A6068F /* GTObjectDatabase.h in Headers */, - 88BC0E5118EF4F3600C7D0E6 /* GTRepository+Reset.h in Headers */, - 04DB464E133AB5E200D9C624 /* NSError+Git.h in Headers */, - 55C8057F13875C62004DCB0F /* NSData+Git.h in Headers */, - 55C8058013875C6E004DCB0F /* NSString+Git.h in Headers */, - 04DB464F133AB5E200D9C624 /* GTRepository.h in Headers */, - 306123AD17EA5261006591D4 /* EXTScope.h in Headers */, - 04DB4652133AB5E200D9C624 /* GTEnumerator.h in Headers */, - 04DB4653133AB5E200D9C624 /* GTTree.h in Headers */, - 5BE612891745EE3400266D8C /* GTTreeBuilder.h in Headers */, - D021DF501806899200934E32 /* NSArray+StringArray.h in Headers */, - 04DB4654133AB5E200D9C624 /* GTTreeEntry.h in Headers */, - 04DB4655133AB5E200D9C624 /* GTBlob.h in Headers */, - 04DB4656133AB5E200D9C624 /* GTTag.h in Headers */, - 04DB4657133AB5E200D9C624 /* GTIndex.h in Headers */, - 04DB4658133AB5E200D9C624 /* GTIndexEntry.h in Headers */, - D0CE552118B6C58F008EB8E0 /* GTFilterList.h in Headers */, - 04DB4659133AB5E200D9C624 /* GTReference.h in Headers */, - 04DB465A133AB5E200D9C624 /* ObjectiveGit.h in Headers */, - 04DB465B133AB5E200D9C624 /* GTCommit.h in Headers */, - 04DB465C133AB5E200D9C624 /* GTObject.h in Headers */, - 04DB465D133AB5E200D9C624 /* GTSignature.h in Headers */, - 04DB465E133AB5E200D9C624 /* GTBranch.h in Headers */, - AA046113134F4D2000DF526B /* GTOdbObject.h in Headers */, - D03B57A218BFFF07007124F4 /* GTDiffPatch.h in Headers */, - 880EE66218AE700500B82455 /* GTFilter.h in Headers */, - E9FFC6C01577CC8A00A9E736 /* GTConfiguration.h in Headers */, - 30DCBA5D17B45213009B0EBD /* GTStatusDelta.h in Headers */, - 30A3D6551667F11C00C49A39 /* GTDiff.h in Headers */, - 3011D86C1668E48500CE3409 /* GTDiffFile.h in Headers */, - 6A74CA3716A942C800E1A3C5 /* GTRemote.h in Headers */, - 3011D8721668E78500CE3409 /* GTDiffHunk.h in Headers */, - 30FDC08016835A8100654BF0 /* GTDiffLine.h in Headers */, - 8821547717147A5200D76B76 /* GTReflogEntry.h in Headers */, - 3011D8781668F29600CE3409 /* GTDiffDelta.h in Headers */, - 398F8AA6183111080071359D /* GTCredential.h in Headers */, - 88746CC517FA1C950005888A /* GTRepository+Committing.h in Headers */, - 30DCBA6417B45A78009B0EBD /* GTRepository+Status.h in Headers */, - 8821547E17147B3600D76B76 /* GTOID.h in Headers */, - 8821546A1714740500D76B76 /* GTReflog.h in Headers */, - 20F43DE418A2F668007D3621 /* GTRepository+Blame.h in Headers */, - 20702DAF18A1F38A009FB457 /* GTBlame.h in Headers */, - 20702DB118A1F38A009FB457 /* GTBlameHunk.h in Headers */, - 6A74CA3616A942C000E1A3C5 /* GTConfiguration+Private.h in Headers */, - 30B1E7EF1703522100D0814D /* NSDate+GTTimeAdditions.h in Headers */, - D09C2E371755F16200065E36 /* GTSubmodule.h in Headers */, - D015F7CB17F695E800AD5E1F /* GTRepository+Stashing.h in Headers */, - 4DE864361794A37E00371A65 /* GTRepository+Private.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 8DC2EF500486A6940098B216 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1005,21 +1342,27 @@ 88BC0E5018EF4F3600C7D0E6 /* GTRepository+Reset.h in Headers */, BDFAF9C3131C1845000508BC /* GTIndex.h in Headers */, BDFAF9C9131C1868000508BC /* GTIndexEntry.h in Headers */, + 6EEB51A1199D62B9001D72C0 /* GTFetchHeadEntry.h in Headers */, + 4DC55AE51AD859AD0032563C /* GTCheckoutOptions.h in Headers */, BD441E08131ED0C300187010 /* GTReference.h in Headers */, 88F6D9D91320451F00CC0BA8 /* ObjectiveGit.h in Headers */, + 88B2131C1B20E785005CF2C5 /* GTRepository+References.h in Headers */, 88F6D9FA1320467100CC0BA8 /* GTCommit.h in Headers */, 88F6D9FB1320467500CC0BA8 /* GTObject.h in Headers */, AA046112134F4D2000DF526B /* GTOdbObject.h in Headers */, + D0A0129519F99EF8007F1914 /* NSDate+GTTimeAdditions.h in Headers */, + 4DFFB15B183AA8D600D1565E /* GTRepository+RemoteOperations.h in Headers */, BDB2B1301386F34300C88D55 /* GTObjectDatabase.h in Headers */, 88F6D9FC1320467800CC0BA8 /* GTSignature.h in Headers */, 88F50F59132054D800584FBE /* GTBranch.h in Headers */, - 306123AC17EA5261006591D4 /* EXTScope.h in Headers */, 79262F8B13C69B1600A4B1EA /* git2.h in Headers */, 88EB7E4D14AEBA600046FEA4 /* GTConfiguration.h in Headers */, BDE4C064130EFE2C00851650 /* NSError+Git.h in Headers */, + 23F39FAD1C86DB1C00849F3C /* GTRepository+Merging.h in Headers */, 55C8057D13875C11004DCB0F /* NSData+Git.h in Headers */, D03B57A118BFFF07007124F4 /* GTDiffPatch.h in Headers */, 883CD6AB1600EBC600F57354 /* GTRemote.h in Headers */, + 88E353001982E9160051001F /* GTRepository+Attributes.h in Headers */, 55C8057E13875C1B004DCB0F /* NSString+Git.h in Headers */, 30A3D6541667F11C00C49A39 /* GTDiff.h in Headers */, 3011D86B1668E48500CE3409 /* GTDiffFile.h in Headers */, @@ -1037,39 +1380,82 @@ 8821547617147A5200D76B76 /* GTReflogEntry.h in Headers */, 30DCBA5C17B45213009B0EBD /* GTStatusDelta.h in Headers */, 88746CC417FA1C950005888A /* GTRepository+Committing.h in Headers */, - 30B1E7EE1703522100D0814D /* NSDate+GTTimeAdditions.h in Headers */, D09C2E361755F16200065E36 /* GTSubmodule.h in Headers */, - 306123B117EA5261006591D4 /* metamacros.h in Headers */, - 4DE864351794A37E00371A65 /* GTRepository+Private.h in Headers */, + F8D1BDEE1B31FE7C00CDEC90 /* GTRepository+Pull.h in Headers */, + F964D5F11CE9D9B200F1D8DD /* GTNote.h in Headers */, 4D79C0EE17DF9F4D00997DE4 /* GTCredential.h in Headers */, - 4D79C0F717DFAA7100997DE4 /* GTCredential+Private.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D01B6ED019F82E2000D411BC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D01B6F3D19F82F8700D411BC /* GTTag.h in Headers */, + D01B6F4119F82F8700D411BC /* GTIndexEntry.h in Headers */, + D01B6F2319F82F8700D411BC /* GTRepository+Reset.h in Headers */, + D01B6F2D19F82F8700D411BC /* GTEnumerator.h in Headers */, + D01B6F5519F82FA600D411BC /* GTReflog.h in Headers */, + 23F39FAF1C86DD0A00849F3C /* GTRepository+Merging.h in Headers */, + D01B6F3519F82F8700D411BC /* GTBlob.h in Headers */, + D01B6F4D19F82F8700D411BC /* GTRemote.h in Headers */, + D01B6F1519F82F7B00D411BC /* NSData+Git.h in Headers */, + D01B6F6119F82FA600D411BC /* GTFilterSource.h in Headers */, + D0E0171519F9AD820019930C /* ObjectiveGit.h in Headers */, + 88B2131D1B20E785005CF2C5 /* GTRepository+References.h in Headers */, + D01B6F4919F82F8700D411BC /* GTOdbObject.h in Headers */, + D01B6F3919F82F8700D411BC /* GTTreeEntry.h in Headers */, + D01B6F5B19F82FA600D411BC /* GTSubmodule.h in Headers */, + D01B6F4719F82F8700D411BC /* GTObjectDatabase.h in Headers */, + D01B6F2B19F82F8700D411BC /* GTRepository+RemoteOperations.h in Headers */, + D01B6F7319F82FB300D411BC /* GTDiffLine.h in Headers */, + D01B6F6B19F82FB300D411BC /* GTDiff.h in Headers */, + D01B6F3119F82F8700D411BC /* GTCommit.h in Headers */, + D01B6F2519F82F8700D411BC /* GTStatusDelta.h in Headers */, + D01B6F6519F82FA600D411BC /* GTRepository+Attributes.h in Headers */, + D01B6F2919F82F8700D411BC /* GTRepository+Committing.h in Headers */, + D01B6F4F19F82FA600D411BC /* GTRepository+Blame.h in Headers */, + D01B6F1719F82F7B00D411BC /* NSError+Git.h in Headers */, + D01B6F3319F82F8700D411BC /* GTSignature.h in Headers */, + D01B6F1919F82F7B00D411BC /* NSString+Git.h in Headers */, + D01B6F7519F82FB300D411BC /* GTDiffPatch.h in Headers */, + D01B6F3719F82F8700D411BC /* GTTree.h in Headers */, + D01B6F5D19F82FA600D411BC /* GTCredential.h in Headers */, + D01B6F5719F82FA600D411BC /* GTReflogEntry.h in Headers */, + D01B6F1F19F82F8700D411BC /* GTRepository.h in Headers */, + D01B6F2119F82F8700D411BC /* GTRepository+Status.h in Headers */, + D01B6F5919F82FA600D411BC /* GTOID.h in Headers */, + D01B6F6D19F82FB300D411BC /* GTDiffFile.h in Headers */, + D01B6F2F19F82F8700D411BC /* GTObject.h in Headers */, + 4DC55AE61AD859AD0032563C /* GTCheckoutOptions.h in Headers */, + D01B6F4B19F82F8700D411BC /* GTConfiguration.h in Headers */, + D01B6F6719F82FA600D411BC /* GTFetchHeadEntry.h in Headers */, + D01B6F5F19F82FA600D411BC /* GTFilter.h in Headers */, + D01B6F5319F82FA600D411BC /* GTBlameHunk.h in Headers */, + D01B6F6F19F82FB300D411BC /* GTDiffHunk.h in Headers */, + D01B6F4319F82F8700D411BC /* GTReference.h in Headers */, + D01B6F3F19F82F8700D411BC /* GTIndex.h in Headers */, + D01B6F2719F82F8700D411BC /* GTRepository+Stashing.h in Headers */, + D01B6F5119F82FA600D411BC /* GTBlame.h in Headers */, + D01B6F1D19F82F7B00D411BC /* NSArray+StringArray.h in Headers */, + D01B6F4519F82F8700D411BC /* GTBranch.h in Headers */, + D01B6F7119F82FB300D411BC /* GTDiffDelta.h in Headers */, + D01B6F3B19F82F8700D411BC /* GTTreeBuilder.h in Headers */, + D01B6F1B19F82F7B00D411BC /* NSDate+GTTimeAdditions.h in Headers */, + F964D5F21CE9D9B200F1D8DD /* GTNote.h in Headers */, + D01B6F6319F82FA600D411BC /* GTFilterList.h in Headers */, + 889923FB19FF5DD40092A9A6 /* git2 in Headers */, + F8D1BDEF1B31FE7C00CDEC90 /* GTRepository+Pull.h in Headers */, + D01B6F1419F82F6000D411BC /* git2.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 04DB4644133AB57600D9C624 /* ObjectiveGit-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 04DB464B133AB58900D9C624 /* Build configuration list for PBXNativeTarget "ObjectiveGit-iOS" */; - buildPhases = ( - 04DB4641133AB57600D9C624 /* Headers */, - 04DB4642133AB57600D9C624 /* Sources */, - 04DB4643133AB57600D9C624 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D0A330FA16027F4D00A616FA /* PBXTargetDependency */, - ); - name = "ObjectiveGit-iOS"; - productName = "ObjectiveGit-iOS"; - productReference = 04DB4645133AB57600D9C624 /* libObjectiveGit-iOS.a */; - productType = "com.apple.product-type.library.static"; - }; - 88F05A6A16011E5400B7AD1D /* ObjectiveGitTests */ = { + 88F05A6A16011E5400B7AD1D /* ObjectiveGit-MacTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 88F05A7F16011E5400B7AD1D /* Build configuration list for PBXNativeTarget "ObjectiveGitTests" */; + buildConfigurationList = 88F05A7F16011E5400B7AD1D /* Build configuration list for PBXNativeTarget "ObjectiveGit-MacTests" */; buildPhases = ( 88F05A6616011E5400B7AD1D /* Sources */, 88F05A6716011E5400B7AD1D /* Frameworks */, @@ -1078,63 +1464,139 @@ buildRules = ( ); dependencies = ( + 4DD769852645D15D007599B8 /* PBXTargetDependency */, + 4DD769872645D15D007599B8 /* PBXTargetDependency */, + 4DD769892645D15D007599B8 /* PBXTargetDependency */, 88F05AA016011F9000B7AD1D /* PBXTargetDependency */, ); - name = ObjectiveGitTests; + name = "ObjectiveGit-MacTests"; productName = ObjectiveGitTests; - productReference = 88F05A6B16011E5400B7AD1D /* ObjectiveGitTests.octest */; - productType = "com.apple.product-type.bundle"; + productReference = 88F05A6B16011E5400B7AD1D /* ObjectiveGit-MacTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; }; - 8DC2EF4F0486A6940098B216 /* ObjectiveGit */ = { + 8DC2EF4F0486A6940098B216 /* ObjectiveGit-Mac */ = { isa = PBXNativeTarget; - buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "ObjectiveGit" */; + buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "ObjectiveGit-Mac" */; buildPhases = ( - 8DC2EF500486A6940098B216 /* Headers */, - 79262F0F13C697BE00A4B1EA /* Copy git2 Headers */, + 8DC2EF540486A6940098B216 /* Sources */, 8DC2EF520486A6940098B216 /* Resources */, 8DC2EF560486A6940098B216 /* Frameworks */, - 8DC2EF540486A6940098B216 /* Sources */, + 79262F0F13C697BE00A4B1EA /* Copy git2 Headers */, + BEF7E4DF1A3A47450035BB8E /* Copy git2 Headers again */, + 8DC2EF500486A6940098B216 /* Headers */, + 4D751E9D215D765D003CD3CE /* Package external libraries */, ); buildRules = ( ); dependencies = ( - D0A330F816027F4900A616FA /* PBXTargetDependency */, + 3D6123BE1A6432F6008F831A /* PBXTargetDependency */, ); - name = ObjectiveGit; + name = "ObjectiveGit-Mac"; productInstallPath = "$(HOME)/Library/Frameworks"; productName = ObjectiveGitFramework; productReference = 8DC2EF5B0486A6940098B216 /* ObjectiveGit.framework */; productType = "com.apple.product-type.framework"; }; + D01B6ED219F82E2000D411BC /* ObjectiveGit-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D01B6EE619F82E2000D411BC /* Build configuration list for PBXNativeTarget "ObjectiveGit-iOS" */; + buildPhases = ( + D01B6ECE19F82E2000D411BC /* Sources */, + D01B6ECF19F82E2000D411BC /* Frameworks */, + D01B6ED019F82E2000D411BC /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + D019779719F8335100F523DA /* PBXTargetDependency */, + ); + name = "ObjectiveGit-iOS"; + productName = ObjectiveGit; + productReference = D01B6ED319F82E2000D411BC /* ObjectiveGit.framework */; + productType = "com.apple.product-type.framework"; + }; + F879D8351B4B7F7C002D5C07 /* ObjectiveGit-iOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F879D83F1B4B7F7D002D5C07 /* Build configuration list for PBXNativeTarget "ObjectiveGit-iOSTests" */; + buildPhases = ( + F879D8321B4B7F7C002D5C07 /* Sources */, + F879D8331B4B7F7C002D5C07 /* Frameworks */, + F879D8341B4B7F7C002D5C07 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 4DD769A42645D16B007599B8 /* PBXTargetDependency */, + 4DD769A62645D16B007599B8 /* PBXTargetDependency */, + 4DD769A82645D16B007599B8 /* PBXTargetDependency */, + F879D83E1B4B7F7D002D5C07 /* PBXTargetDependency */, + ); + name = "ObjectiveGit-iOSTests"; + productName = "ObjectiveGit-iOSTests"; + productReference = F879D8361B4B7F7C002D5C07 /* ObjectiveGit-iOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 0700; LastTestingUpgradeCheck = 0510; - LastUpgradeCheck = 0510; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = "GitHub, Inc"; + TargetAttributes = { + 88F05A6A16011E5400B7AD1D = { + LastSwiftMigration = 0830; + }; + 8DC2EF4F0486A6940098B216 = { + LastSwiftMigration = 0800; + }; + D01B6ED219F82E2000D411BC = { + CreatedOnToolsVersion = 6.1; + LastSwiftMigration = 0800; + }; + F879D8351B4B7F7C002D5C07 = { + CreatedOnToolsVersion = 6.4; + LastSwiftMigration = 0930; + }; + }; }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "ObjectiveGitFramework" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 1; knownRegions = ( - English, - Japanese, - French, - German, en, + ja, + fr, + de, + Base, ); mainGroup = 0867D691FE84028FC02AAC07 /* ObjectiveGitFramework */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 4DB970F82645C8F300D14944 /* Products */; + ProjectRef = 4DB970F72645C8F300D14944 /* Nimble.xcodeproj */; + }, + { + ProductGroup = 4DB970922645C8BA00D14944 /* Products */; + ProjectRef = 4DB970912645C8BA00D14944 /* Quick.xcodeproj */; + }, + { + ProductGroup = 4DB9710E2645C8F300D14944 /* Products */; + ProjectRef = 4DB9710D2645C8F300D14944 /* ZipArchive.xcodeproj */; + }, + ); projectRoot = ""; targets = ( - 8DC2EF4F0486A6940098B216 /* ObjectiveGit */, - 04DB4644133AB57600D9C624 /* ObjectiveGit-iOS */, - 88F05A6A16011E5400B7AD1D /* ObjectiveGitTests */, + 8DC2EF4F0486A6940098B216 /* ObjectiveGit-Mac */, + 88F05A6A16011E5400B7AD1D /* ObjectiveGit-MacTests */, + D01B6ED219F82E2000D411BC /* ObjectiveGit-iOS */, + F879D8351B4B7F7C002D5C07 /* ObjectiveGit-iOSTests */, D0A330ED16027F1E00A616FA /* libgit2 */, D0A330F216027F3600A616FA /* libgit2-iOS */, 6A28265217C69CB400C6A948 /* OpenSSL-iOS */, @@ -1143,12 +1605,175 @@ }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + 4DB970A32645C8BB00D14944 /* Quick.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Quick.framework; + remoteRef = 4DB970A22645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970A52645C8BB00D14944 /* Quick - macOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Quick - macOSTests.xctest"; + remoteRef = 4DB970A42645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970A72645C8BB00D14944 /* QuickIssue853RegressionTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = QuickIssue853RegressionTests.xctest; + remoteRef = 4DB970A62645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970A92645C8BB00D14944 /* QuickFocused - macOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "QuickFocused - macOSTests.xctest"; + remoteRef = 4DB970A82645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970AB2645C8BB00D14944 /* QuickAfterSuite - macOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "QuickAfterSuite - macOSTests.xctest"; + remoteRef = 4DB970AA2645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970AD2645C8BB00D14944 /* Quick.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Quick.framework; + remoteRef = 4DB970AC2645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970AF2645C8BB00D14944 /* Quick - iOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Quick - iOSTests.xctest"; + remoteRef = 4DB970AE2645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970B12645C8BB00D14944 /* QuickFocused - iOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "QuickFocused - iOSTests.xctest"; + remoteRef = 4DB970B02645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970B32645C8BB00D14944 /* QuickAfterSuite - iOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "QuickAfterSuite - iOSTests.xctest"; + remoteRef = 4DB970B22645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970B52645C8BB00D14944 /* Quick.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Quick.framework; + remoteRef = 4DB970B42645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970B72645C8BB00D14944 /* Quick - tvOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "Quick - tvOSTests.xctest"; + remoteRef = 4DB970B62645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970B92645C8BB00D14944 /* QuickFocused - tvOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "QuickFocused - tvOSTests.xctest"; + remoteRef = 4DB970B82645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB970BB2645C8BB00D14944 /* QuickAfterSuite - tvOSTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = "QuickAfterSuite - tvOSTests.xctest"; + remoteRef = 4DB970BA2645C8BB00D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB971022645C8F300D14944 /* Nimble.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Nimble.framework; + remoteRef = 4DB971012645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB971042645C8F300D14944 /* NimbleTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = NimbleTests.xctest; + remoteRef = 4DB971032645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB971062645C8F300D14944 /* Nimble.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Nimble.framework; + remoteRef = 4DB971052645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB971082645C8F300D14944 /* NimbleTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = NimbleTests.xctest; + remoteRef = 4DB971072645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB9710A2645C8F300D14944 /* Nimble.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Nimble.framework; + remoteRef = 4DB971092645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB9710C2645C8F300D14944 /* NimbleTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = NimbleTests.xctest; + remoteRef = 4DB9710B2645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB971152645C8F300D14944 /* ZipArchive.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = ZipArchive.framework; + remoteRef = 4DB971142645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB971172645C8F300D14944 /* ZipArchive.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = ZipArchive.framework; + remoteRef = 4DB971162645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB971192645C8F300D14944 /* ZipArchive.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = ZipArchive.framework; + remoteRef = 4DB971182645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4DB9711B2645C8F300D14944 /* ZipArchive.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = ZipArchive.framework; + remoteRef = 4DB9711A2645C8F300D14944 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ 88F05A6816011E5400B7AD1D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 88F05AC41601204200B7AD1D /* InfoPlist.strings in Resources */, D09C2E51175602A500065E36 /* fixtures.zip in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1157,59 +1782,84 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F879D8341B4B7F7C002D5C07 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F8D007A81B4FA045009A8DAF /* fixtures.zip in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 6A28265317C69CB400C6A948 /* ShellScript */ = { + 4D751E9D215D765D003CD3CE /* Package external libraries */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Package external libraries"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "./script/repackage-dylibs.rb"; + }; + 6A28265317C69CB400C6A948 /* OpenSSL-iOS */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "OpenSSL-iOS"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = script/update_libssl_ios; }; - 6A3C609117D5963700382DFF /* ShellScript */ = { + 6A3C609117D5963700382DFF /* libssh2-iOS */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "libssh2-iOS"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = script/update_libssh2_ios; }; - D0A330F116027F2300A616FA /* ShellScript */ = { + D0A330F116027F2300A616FA /* libgit2 */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = libgit2; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = script/update_libgit2; }; - D0A330F616027F3B00A616FA /* ShellScript */ = { + D0A330F616027F3B00A616FA /* libgit2-iOS */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "libgit2-iOS"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1219,99 +1869,48 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 04DB4642133AB57600D9C624 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6A74CA3816A9432A00E1A3C5 /* ObjectiveGit.m in Sources */, - 886E622D18AEBF75000611A0 /* GTFilterSource.m in Sources */, - 6A74CA3416A942AA00E1A3C5 /* NSData+Git.m in Sources */, - 6A74CA3216A9429700E1A3C5 /* GTRemote.m in Sources */, - 04DB4660133AB5EB00D9C624 /* NSError+Git.m in Sources */, - 04DB4661133AB5EB00D9C624 /* GTRepository.m in Sources */, - 04DB4664133AB5EB00D9C624 /* GTEnumerator.m in Sources */, - 398F8AA7183111260071359D /* GTCredential.m in Sources */, - 04DB4665133AB5EB00D9C624 /* GTCommit.m in Sources */, - 04DB4666133AB5EB00D9C624 /* GTObject.m in Sources */, - 04DB4667133AB5EB00D9C624 /* GTSignature.m in Sources */, - 04DB4668133AB5EB00D9C624 /* GTTree.m in Sources */, - DD3D9520182AB3C4004AF532 /* GTBlameHunk.m in Sources */, - 04DB4669133AB5EB00D9C624 /* GTTreeEntry.m in Sources */, - 04DB466A133AB5EB00D9C624 /* GTBlob.m in Sources */, - 306123B017EA5261006591D4 /* EXTScope.m in Sources */, - 04DB466B133AB5EB00D9C624 /* GTTag.m in Sources */, - 04DB466C133AB5EB00D9C624 /* GTIndex.m in Sources */, - 30DCBA7517B4791A009B0EBD /* NSArray+StringArray.m in Sources */, - 30DCBA6717B45A78009B0EBD /* GTRepository+Status.m in Sources */, - 04DB466D133AB5EB00D9C624 /* GTIndexEntry.m in Sources */, - 04DB466E133AB5EB00D9C624 /* GTReference.m in Sources */, - 04DB466F133AB5EB00D9C624 /* GTBranch.m in Sources */, - AA046115134F4D2000DF526B /* GTOdbObject.m in Sources */, - 55C8054F13861FE7004DCB0F /* GTObjectDatabase.m in Sources */, - 55C8057C13875579004DCB0F /* NSString+Git.m in Sources */, - E9FFC6BF1577CC8300A9E736 /* GTConfiguration.m in Sources */, - D0CE552418B6C58F008EB8E0 /* GTFilterList.m in Sources */, - 88746CC717FA1C950005888A /* GTRepository+Committing.m in Sources */, - 30A3D6571667F11C00C49A39 /* GTDiff.m in Sources */, - 3011D86E1668E48500CE3409 /* GTDiffFile.m in Sources */, - 3011D8741668E78500CE3409 /* GTDiffHunk.m in Sources */, - 3011D87A1668F29600CE3409 /* GTDiffDelta.m in Sources */, - DD3D951E182AB3BD004AF532 /* GTBlame.m in Sources */, - 880EE66418AE700500B82455 /* GTFilter.m in Sources */, - 88BC0E5318EF4F3600C7D0E6 /* GTRepository+Reset.m in Sources */, - 30FDC08216835A8100654BF0 /* GTDiffLine.m in Sources */, - 30DCBA6017B45213009B0EBD /* GTStatusDelta.m in Sources */, - 30B1E7F11703522100D0814D /* NSDate+GTTimeAdditions.m in Sources */, - 8821546C1714740500D76B76 /* GTReflog.m in Sources */, - 8821547917147A5200D76B76 /* GTReflogEntry.m in Sources */, - D03B57A518BFFF07007124F4 /* GTDiffPatch.m in Sources */, - 8821548017147B3600D76B76 /* GTOID.m in Sources */, - 5BE6128B1745EE3400266D8C /* GTTreeBuilder.m in Sources */, - D015F7CD17F695E800AD5E1F /* GTRepository+Stashing.m in Sources */, - 20F43DE718A2F668007D3621 /* GTRepository+Blame.m in Sources */, - D09C2E391755F16200065E36 /* GTSubmodule.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 88F05A6616011E5400B7AD1D /* Sources */ = { + 88F05A6616011E5400B7AD1D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F9D1D4251CEB7BA6009E5855 /* GTNoteSpec.m in Sources */, + 23BB67C11C7DF60300A37A66 /* GTRepository+PullSpec.m in Sources */, D0751CD918BE520400134314 /* GTFilterListSpec.m in Sources */, 200578C518932A82001C06C3 /* GTBlameSpec.m in Sources */, - 88F05AC816012CEE00B7AD1D /* NSData+Git.m in Sources */, - 88F05AC716012CE500B7AD1D /* NSString+Git.m in Sources */, 4D1C40D8182C006D00BE2960 /* GTBlobSpec.m in Sources */, 88F05AB516011FFD00B7AD1D /* GTCommitSpec.m in Sources */, - 88F05AB916011FFD00B7AD1D /* GTObjectTest.m in Sources */, + D0A0129719F9A660007F1914 /* SwiftSpec.swift in Sources */, + 88F05AB916011FFD00B7AD1D /* GTObjectSpec.m in Sources */, 30865A91167F503400B1AB6E /* GTDiffSpec.m in Sources */, 88A994BA16FCE7D400402C7B /* GTBranchSpec.m in Sources */, 2089E43C17D9A58000F451DA /* GTTagSpec.m in Sources */, D015F7D517F6965400AD5E1F /* GTRepositoryStashingSpec.m in Sources */, - 88A994CB16FCED1D00402C7B /* GTTestCase.m in Sources */, + 88A994CB16FCED1D00402C7B /* QuickSpec+GTFixtures.m in Sources */, + F8EFA03A1B4059ED000FF7D0 /* GTUtilityFunctions.m in Sources */, 30B1E8001703871900D0814D /* GTTimeAdditionsSpec.m in Sources */, - 30DCBA6617B45A78009B0EBD /* GTRepository+Status.m in Sources */, + 8870390B1975E3F2004118D7 /* GTDiffDeltaSpec.m in Sources */, 88C0BC5917038CF3009E99AA /* GTConfigurationSpec.m in Sources */, - 30DCBA5F17B45213009B0EBD /* GTStatusDelta.m in Sources */, 88215483171499BE00D76B76 /* GTReflogSpec.m in Sources */, 88948AC91779243600809CDA /* GTObjectDatabaseSpec.m in Sources */, 886E623718AECD86000611A0 /* GTFilterSpec.m in Sources */, D0AC906C172F941F00347DC4 /* GTRepositorySpec.m in Sources */, - 30DCBA7417B4791A009B0EBD /* NSArray+StringArray.m in Sources */, - 306123AF17EA5261006591D4 /* EXTScope.m in Sources */, + D01EFDA0195DEF2200838D24 /* NSDataGitSpec.m in Sources */, 30A269AD17B4878C000FE64E /* GTRepository+StatusSpec.m in Sources */, 307623AB17C6C8BD00E2CDF1 /* NSArray+StringArraySpec.m in Sources */, 8832811F173D8816006D7DCF /* GTIndexSpec.m in Sources */, D0F4E28A17C7F24200BBDE30 /* NSErrorGitSpec.m in Sources */, + F879D8311B4B788F002D5C07 /* Libgit2FeaturesSpec.m in Sources */, 88328128173D8A64006D7DCF /* GTTreeSpec.m in Sources */, + 88E353061982EA6B0051001F /* GTRepositoryAttributesSpec.m in Sources */, 88234B2618F2FE260039972E /* GTRepositoryResetSpec.m in Sources */, 5BE612931745EEBC00266D8C /* GTTreeBuilderSpec.m in Sources */, + F8E4A2911A170CA6006485A8 /* GTRemotePushSpec.m in Sources */, D06D9E011755D10000558C17 /* GTEnumeratorSpec.m in Sources */, D03B7C411756AB370034A610 /* GTSubmoduleSpec.m in Sources */, D00F6816175D373C004DB9D6 /* GTReferenceSpec.m in Sources */, D040AF70177B9779001AD9EB /* GTOIDSpec.m in Sources */, - F6ED8DA1180E713200A32D40 /* GTRemoteSpec.m in Sources */, D040AF78177B9A9E001AD9EB /* GTSignatureSpec.m in Sources */, + 4DBA4A3217DA73CE006CD5F5 /* GTRemoteSpec.m in Sources */, 4D123240178E009E0048F785 /* GTRepositoryCommittingSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1321,17 +1920,21 @@ buildActionMask = 2147483647; files = ( BDE4C065130EFE2C00851650 /* NSError+Git.m in Sources */, + 88E353021982E9160051001F /* GTRepository+Attributes.m in Sources */, BDE4C067130EFE2C00851650 /* GTRepository.m in Sources */, BDD8AE7013131B8800CB5D40 /* GTEnumerator.m in Sources */, BD6C235313146E6600992935 /* GTCommit.m in Sources */, 30DCBA5E17B45213009B0EBD /* GTStatusDelta.m in Sources */, + 23F39FAE1C86DB1C00849F3C /* GTRepository+Merging.m in Sources */, 30DCBA6517B45A78009B0EBD /* GTRepository+Status.m in Sources */, BD6C235413146E6A00992935 /* GTObject.m in Sources */, + 4DC55AE71AD859AD0032563C /* GTCheckoutOptions.m in Sources */, BD6C254613148DD300992935 /* GTSignature.m in Sources */, BD6B0412131496B8001909D0 /* GTTree.m in Sources */, BD6B0418131496CC001909D0 /* GTTreeEntry.m in Sources */, DD3D951D182AB25C004AF532 /* GTBlameHunk.m in Sources */, 306123AE17EA5261006591D4 /* EXTScope.m in Sources */, + F8D1BDF01B31FE7C00CDEC90 /* GTRepository+Pull.m in Sources */, BDD6279A1318391200DE34D1 /* GTBlob.m in Sources */, BDD62925131C03D600DE34D1 /* GTTag.m in Sources */, BDFAF9C4131C1845000508BC /* GTIndex.m in Sources */, @@ -1347,21 +1950,26 @@ 88EB7E4E14AEBA600046FEA4 /* GTConfiguration.m in Sources */, 883CD6AC1600EBC600F57354 /* GTRemote.m in Sources */, 30DCBA7317B4791A009B0EBD /* NSArray+StringArray.m in Sources */, + 4DFFB15C183AA8D600D1565E /* GTRepository+RemoteOperations.m in Sources */, 88F05AC61601209A00B7AD1D /* ObjectiveGit.m in Sources */, 30A3D6561667F11C00C49A39 /* GTDiff.m in Sources */, 3011D86D1668E48500CE3409 /* GTDiffFile.m in Sources */, 3011D8731668E78500CE3409 /* GTDiffHunk.m in Sources */, 3011D8791668F29600CE3409 /* GTDiffDelta.m in Sources */, + 6EEB51A2199D62B9001D72C0 /* GTFetchHeadEntry.m in Sources */, + 88B2131E1B20E785005CF2C5 /* GTRepository+References.m in Sources */, 30FDC08116835A8100654BF0 /* GTDiffLine.m in Sources */, 886E622C18AEBF75000611A0 /* GTFilterSource.m in Sources */, DD3D9513182A81E1004AF532 /* GTBlame.m in Sources */, 88746CC617FA1C950005888A /* GTRepository+Committing.m in Sources */, D015F7CC17F695E800AD5E1F /* GTRepository+Stashing.m in Sources */, 30B1E7F01703522100D0814D /* NSDate+GTTimeAdditions.m in Sources */, + F964D5F31CE9D9B200F1D8DD /* GTNote.m in Sources */, 8821546B1714740500D76B76 /* GTReflog.m in Sources */, 8821547817147A5200D76B76 /* GTReflogEntry.m in Sources */, 8821547F17147B3600D76B76 /* GTOID.m in Sources */, D03B57A418BFFF07007124F4 /* GTDiffPatch.m in Sources */, + D03B07F71965DAB0009E5624 /* NSData+Git.m in Sources */, 20F43DE618A2F668007D3621 /* GTRepository+Blame.m in Sources */, 5BE6128A1745EE3400266D8C /* GTTreeBuilder.m in Sources */, D09C2E381755F16200065E36 /* GTSubmodule.m in Sources */, @@ -1369,13 +1977,150 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D01B6ECE19F82E2000D411BC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D01B6F7419F82FB300D411BC /* GTDiffLine.m in Sources */, + D01B6F3C19F82F8700D411BC /* GTTreeBuilder.m in Sources */, + D01B6F4219F82F8700D411BC /* GTIndexEntry.m in Sources */, + D01B6F2E19F82F8700D411BC /* GTEnumerator.m in Sources */, + D01B6F4C19F82F8700D411BC /* GTConfiguration.m in Sources */, + D01B6F6619F82FA600D411BC /* GTRepository+Attributes.m in Sources */, + 23F39FB01C86E01800849F3C /* GTRepository+Merging.m in Sources */, + D019778A19F8307600F523DA /* ObjectiveGit.m in Sources */, + D01B6F3219F82F8700D411BC /* GTCommit.m in Sources */, + 4DC55AE81AD859AD0032563C /* GTCheckoutOptions.m in Sources */, + D01B6F3819F82F8700D411BC /* GTTree.m in Sources */, + D01B6F6C19F82FB300D411BC /* GTDiff.m in Sources */, + 884C8A3A19FF4B890017E98D /* EXTScope.m in Sources */, + D01B6F4E19F82F8700D411BC /* GTRemote.m in Sources */, + D01B6F3019F82F8700D411BC /* GTObject.m in Sources */, + F8D1BDF11B31FE7C00CDEC90 /* GTRepository+Pull.m in Sources */, + D01B6F4619F82F8700D411BC /* GTBranch.m in Sources */, + D01B6F3A19F82F8700D411BC /* GTTreeEntry.m in Sources */, + D01B6F2419F82F8700D411BC /* GTRepository+Reset.m in Sources */, + D01B6F5219F82FA600D411BC /* GTBlame.m in Sources */, + D01B6F5A19F82FA600D411BC /* GTOID.m in Sources */, + D01B6F2219F82F8700D411BC /* GTRepository+Status.m in Sources */, + D01B6F3419F82F8700D411BC /* GTSignature.m in Sources */, + D01B6F4419F82F8700D411BC /* GTReference.m in Sources */, + D01B6F6019F82FA600D411BC /* GTFilter.m in Sources */, + D01B6F1819F82F7B00D411BC /* NSError+Git.m in Sources */, + D01B6F2A19F82F8700D411BC /* GTRepository+Committing.m in Sources */, + D01B6F5019F82FA600D411BC /* GTRepository+Blame.m in Sources */, + D01B6F5C19F82FA600D411BC /* GTSubmodule.m in Sources */, + D01B6F4019F82F8700D411BC /* GTIndex.m in Sources */, + D01B6F7219F82FB300D411BC /* GTDiffDelta.m in Sources */, + D01B6F6419F82FA600D411BC /* GTFilterList.m in Sources */, + D01B6F2819F82F8700D411BC /* GTRepository+Stashing.m in Sources */, + D01B6F7619F82FB300D411BC /* GTDiffPatch.m in Sources */, + D01B6F3E19F82F8700D411BC /* GTTag.m in Sources */, + D01B6F1A19F82F7B00D411BC /* NSString+Git.m in Sources */, + D01B6F2619F82F8700D411BC /* GTStatusDelta.m in Sources */, + D01B6F2019F82F8700D411BC /* GTRepository.m in Sources */, + 88B2131F1B20E785005CF2C5 /* GTRepository+References.m in Sources */, + D01B6F7019F82FB300D411BC /* GTDiffHunk.m in Sources */, + D01B6F4819F82F8700D411BC /* GTObjectDatabase.m in Sources */, + D01B6F2C19F82F8700D411BC /* GTRepository+RemoteOperations.m in Sources */, + D01B6F5419F82FA600D411BC /* GTBlameHunk.m in Sources */, + D01B6F6819F82FA600D411BC /* GTFetchHeadEntry.m in Sources */, + D01B6F3619F82F8700D411BC /* GTBlob.m in Sources */, + F964D5F51CE9D9B200F1D8DD /* GTNote.m in Sources */, + D01B6F6E19F82FB300D411BC /* GTDiffFile.m in Sources */, + D01B6F5619F82FA600D411BC /* GTReflog.m in Sources */, + D01B6F5E19F82FA600D411BC /* GTCredential.m in Sources */, + D01B6F6219F82FA600D411BC /* GTFilterSource.m in Sources */, + D01B6F1C19F82F7B00D411BC /* NSDate+GTTimeAdditions.m in Sources */, + D01B6F1619F82F7B00D411BC /* NSData+Git.m in Sources */, + D01B6F1E19F82F7B00D411BC /* NSArray+StringArray.m in Sources */, + D01B6F5819F82FA600D411BC /* GTReflogEntry.m in Sources */, + D01B6F4A19F82F8700D411BC /* GTOdbObject.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F879D8321B4B7F7C002D5C07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F8D007931B4FA03B009A8DAF /* GTObjectSpec.m in Sources */, + F8D0078D1B4FA03B009A8DAF /* GTBranchSpec.m in Sources */, + F8D007761B4F7D10009A8DAF /* GTTimeAdditionsSpec.m in Sources */, + F8D007921B4FA03B009A8DAF /* GTIndexSpec.m in Sources */, + F8D007711B4F7CB0009A8DAF /* NSDataGitSpec.m in Sources */, + 23BB67C21C7DF60400A37A66 /* GTRepository+PullSpec.m in Sources */, + F8D007A11B4FA03B009A8DAF /* GTRepositoryStashingSpec.m in Sources */, + F8D007A31B4FA03B009A8DAF /* GTFilterListSpec.m in Sources */, + F8D0079D1B4FA03B009A8DAF /* GTTreeBuilderSpec.m in Sources */, + F8D007721B4F7CB6009A8DAF /* NSArray+StringArraySpec.m in Sources */, + F8D007731B4F7CC3009A8DAF /* GTSignatureSpec.m in Sources */, + F8D007A21B4FA03B009A8DAF /* GTFilterSpec.m in Sources */, + F81B6B59207B0D3B00AB0836 /* SwiftSpec.swift in Sources */, + F8D007971B4FA03B009A8DAF /* GTRemoteSpec.m in Sources */, + F8D007A61B4FA03B009A8DAF /* GTRepositoryAttributesSpec.m in Sources */, + F8D007941B4FA03B009A8DAF /* GTReferenceSpec.m in Sources */, + F8D007981B4FA03B009A8DAF /* GTBlameSpec.m in Sources */, + F8D007911B4FA03B009A8DAF /* GTEnumeratorSpec.m in Sources */, + F8D007701B4F7CA8009A8DAF /* NSErrorGitSpec.m in Sources */, + F8D007A41B4FA03B009A8DAF /* GTRepositoryResetSpec.m in Sources */, + F8D007951B4FA03B009A8DAF /* GTReflogSpec.m in Sources */, + F8D0079C1B4FA03B009A8DAF /* GTTagSpec.m in Sources */, + F8D007741B4F7CCC009A8DAF /* GTOIDSpec.m in Sources */, + F8D007901B4FA03B009A8DAF /* GTDiffSpec.m in Sources */, + F8D007A71B4FA040009A8DAF /* QuickSpec+GTFixtures.m in Sources */, + F8D0079A1B4FA03B009A8DAF /* GTRepositoryCommittingSpec.m in Sources */, + F8D0078E1B4FA03B009A8DAF /* GTCommitSpec.m in Sources */, + F86949AA1BF1B79E00A989D3 /* GTUtilityFunctions.m in Sources */, + F8D0078F1B4FA03B009A8DAF /* GTConfigurationSpec.m in Sources */, + F8D0079B1B4FA03B009A8DAF /* GTSubmoduleSpec.m in Sources */, + F879D8441B4B80C7002D5C07 /* Libgit2FeaturesSpec.m in Sources */, + F8D0078C1B4FA03B009A8DAF /* GTBlobSpec.m in Sources */, + F8D007991B4FA03B009A8DAF /* GTRepositorySpec.m in Sources */, + F8D0079F1B4FA03B009A8DAF /* GTObjectDatabaseSpec.m in Sources */, + F8D0079E1B4FA03B009A8DAF /* GTTreeSpec.m in Sources */, + F8D007A01B4FA03B009A8DAF /* GTRepository+StatusSpec.m in Sources */, + F8D007961B4FA03B009A8DAF /* GTRemotePushSpec.m in Sources */, + F8D007A51B4FA03B009A8DAF /* GTDiffDeltaSpec.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 6A28265B17C69D6300C6A948 /* PBXTargetDependency */ = { + 3D6123BE1A6432F6008F831A /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 6A28265217C69CB400C6A948 /* OpenSSL-iOS */; - targetProxy = 6A28265A17C69D6300C6A948 /* PBXContainerItemProxy */; + target = D0A330ED16027F1E00A616FA /* libgit2 */; + targetProxy = 3D6123BD1A6432F6008F831A /* PBXContainerItemProxy */; + }; + 4DD769852645D15D007599B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Quick-macOS"; + targetProxy = 4DD769842645D15D007599B8 /* PBXContainerItemProxy */; + }; + 4DD769872645D15D007599B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Nimble-macOS"; + targetProxy = 4DD769862645D15D007599B8 /* PBXContainerItemProxy */; + }; + 4DD769892645D15D007599B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "ZipArchive-Mac"; + targetProxy = 4DD769882645D15D007599B8 /* PBXContainerItemProxy */; + }; + 4DD769A42645D16B007599B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Quick-iOS"; + targetProxy = 4DD769A32645D16B007599B8 /* PBXContainerItemProxy */; + }; + 4DD769A62645D16B007599B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Nimble-iOS"; + targetProxy = 4DD769A52645D16B007599B8 /* PBXContainerItemProxy */; + }; + 4DD769A82645D16B007599B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "ZipArchive-iOS"; + targetProxy = 4DD769A72645D16B007599B8 /* PBXContainerItemProxy */; }; 6A3C609D17D5964E00382DFF /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -1389,147 +2134,77 @@ }; 88F05AA016011F9000B7AD1D /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 8DC2EF4F0486A6940098B216 /* ObjectiveGit */; + target = 8DC2EF4F0486A6940098B216 /* ObjectiveGit-Mac */; targetProxy = 88F05A9F16011F9000B7AD1D /* PBXContainerItemProxy */; }; - D0A330F816027F4900A616FA /* PBXTargetDependency */ = { + D019779719F8335100F523DA /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D0A330ED16027F1E00A616FA /* libgit2 */; - targetProxy = D0A330F716027F4900A616FA /* PBXContainerItemProxy */; + platformFilter = ios; + target = D0A330F216027F3600A616FA /* libgit2-iOS */; + targetProxy = D019779619F8335100F523DA /* PBXContainerItemProxy */; }; - D0A330FA16027F4D00A616FA /* PBXTargetDependency */ = { + F879D83E1B4B7F7D002D5C07 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = D0A330F216027F3600A616FA /* libgit2-iOS */; - targetProxy = D0A330F916027F4D00A616FA /* PBXContainerItemProxy */; + target = D01B6ED219F82E2000D411BC /* ObjectiveGit-iOS */; + targetProxy = F879D83D1B4B7F7D002D5C07 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ -/* Begin PBXVariantGroup section */ - 089C1666FE841158C02AAC07 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 089C1667FE841158C02AAC07 /* English */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - 88F05A7816011E5400B7AD1D /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 88F05A7916011E5400B7AD1D /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ - 04DB4646133AB57600D9C624 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D0D81865174421EB00995A2E /* iOS-StaticLibrary.xcconfig */; - buildSettings = { - GCC_PREFIX_HEADER = ObjectiveGitFramework_Prefix.pch; - HEADER_SEARCH_PATHS = ( - External/libgit2/include, - "$(OBJROOT)/UninstalledProducts/include", - "External/libssh2-ios/include/libssh2", - ); - LIBRARY_SEARCH_PATHS = ( - "External/ios-openssl/lib", - "External/libssh2-ios/lib", - "$(inherited)", - ); - ONLY_ACTIVE_ARCH = NO; - OTHER_CFLAGS = ( - "$(inherited)", - "-DGIT_SSH", - ); - OTHER_LDFLAGS = ( - "-lgit2-ios", - "-all_load", - "-lssh2-ios", - ); - PRIVATE_HEADERS_FOLDER_PATH = include/ObjectiveGit; - PRODUCT_NAME = "ObjectiveGit-iOS"; - PUBLIC_HEADERS_FOLDER_PATH = include/ObjectiveGit; - USER_HEADER_SEARCH_PATHS = ""; - }; - name = Debug; - }; - 04DB4647133AB57600D9C624 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = D0D81865174421EB00995A2E /* iOS-StaticLibrary.xcconfig */; - buildSettings = { - GCC_PREFIX_HEADER = ObjectiveGitFramework_Prefix.pch; - HEADER_SEARCH_PATHS = ( - External/libgit2/include, - "$(OBJROOT)/UninstalledProducts/include", - "External/libssh2-ios/include/libssh2", - ); - LIBRARY_SEARCH_PATHS = ( - "External/ios-openssl/lib", - "External/libssh2-ios/lib", - "$(inherited)", - ); - ONLY_ACTIVE_ARCH = NO; - OTHER_CFLAGS = ( - "$(inherited)", - "-DGIT_SSH", - ); - OTHER_LDFLAGS = ( - "-lgit2-ios", - "-all_load", - "-lssh2-ios", - ); - PRIVATE_HEADERS_FOLDER_PATH = include/ObjectiveGit; - PRODUCT_NAME = "ObjectiveGit-iOS"; - PUBLIC_HEADERS_FOLDER_PATH = include/ObjectiveGit; - USER_HEADER_SEARCH_PATHS = ""; - }; - name = Release; - }; 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D8186A174421EB00995A2E /* Mac-Framework.xcconfig */; + baseConfigurationReference = 4DB97075264596AE00D14944 /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; - GCC_PREFIX_HEADER = ObjectiveGitFramework_Prefix.pch; INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/opt/openssl/lib, + "$(PROJECT_DIR)/External/build/lib", + "$(PROJECT_DIR)/External", + ); + MODULEMAP_FILE = ObjectiveGit.modulemap; OTHER_LDFLAGS = ( - "-lgit2", "-force_load", - External/libgit2.a, + "External/libgit2-mac.a", /usr/local/lib/libssh2.a, "-lcrypto", "-lssl", + "-lcurl", ); + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = ObjectiveGit; - USER_HEADER_SEARCH_PATHS = ""; WRAPPER_EXTENSION = framework; }; name = Debug; }; 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D8186A174421EB00995A2E /* Mac-Framework.xcconfig */; + baseConfigurationReference = 4DB97075264596AE00D14944 /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; - GCC_PREFIX_HEADER = ObjectiveGitFramework_Prefix.pch; INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/opt/openssl/lib, + "$(PROJECT_DIR)/External/build/lib", + "$(PROJECT_DIR)/External", + ); + MODULEMAP_FILE = ObjectiveGit.modulemap; OTHER_LDFLAGS = ( - "-lgit2", "-force_load", - External/libgit2.a, + "External/libgit2-mac.a", /usr/local/lib/libssh2.a, "-lcrypto", "-lssl", + "-lcurl", ); + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = ObjectiveGit; - USER_HEADER_SEARCH_PATHS = ""; WRAPPER_EXTENSION = framework; }; name = Release; @@ -1538,24 +2213,39 @@ isa = XCBuildConfiguration; baseConfigurationReference = D0A463D817E57C45000F5021 /* Debug.xcconfig */; buildSettings = { - GCC_STRICT_ALIASING = NO; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; HEADER_SEARCH_PATHS = ( External/libgit2/include, /usr/local/include, ); - IPHONEOS_DEPLOYMENT_TARGET = 6.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LIBRARY_SEARCH_PATHS = ( ., External, ); - MACOSX_DEPLOYMENT_TARGET = 10.7; + MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "$(inherited)", "-DGIT_SSH", ); - TEST_AFTER_BUILD = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-incomplete-umbrella", + ); }; name = Debug; }; @@ -1563,23 +2253,37 @@ isa = XCBuildConfiguration; baseConfigurationReference = D0A463DA17E57C45000F5021 /* Release.xcconfig */; buildSettings = { - GCC_STRICT_ALIASING = NO; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; HEADER_SEARCH_PATHS = ( External/libgit2/include, /usr/local/include, ); - IPHONEOS_DEPLOYMENT_TARGET = 6.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LIBRARY_SEARCH_PATHS = ( ., External, ); - MACOSX_DEPLOYMENT_TARGET = 10.7; + MACOSX_DEPLOYMENT_TARGET = 10.10; OTHER_CFLAGS = ( "$(inherited)", "-DGIT_SSH", ); - TEST_AFTER_BUILD = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-incomplete-umbrella", + ); }; name = Release; }; @@ -1633,140 +2337,358 @@ }; 88F05A8016011E5400B7AD1D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D81867174421EB00995A2E /* Mac-Application.xcconfig */; + baseConfigurationReference = 4DB97078264596AE00D14944 /* macOS-XCTest.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\""; - GCC_PREFIX_HEADER = "ObjectiveGitTests/ObjectiveGitTests-Prefix.pch"; - HEADER_SEARCH_PATHS = ( + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - ObjectiveGitTests/specta/src, - "ObjectiveGitTests/expecta/src/**", + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "NOUNCRYPT=1", + "NOCRYPT=1", + ); + HEADER_SEARCH_PATHS = ( + External/libgit2/include, + /usr/local/include, ); INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; - OTHER_LDFLAGS = "-all_load"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = octest; }; name = Debug; }; 88F05A8116011E5400B7AD1D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D81867174421EB00995A2E /* Mac-Application.xcconfig */; + baseConfigurationReference = 4DB97078264596AE00D14944 /* macOS-XCTest.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\""; - GCC_PREFIX_HEADER = "ObjectiveGitTests/ObjectiveGitTests-Prefix.pch"; - HEADER_SEARCH_PATHS = ( + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - ObjectiveGitTests/specta/src, - "ObjectiveGitTests/expecta/src/**", + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); + HEADER_SEARCH_PATHS = ( + External/libgit2/include, + /usr/local/include, ); INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; - OTHER_LDFLAGS = "-all_load"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = octest; }; name = Release; }; - D03FC3D71602E97F00BCFA73 /* Profile */ = { + D019778D19F830F500F523DA /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0A463D917E57C45000F5021 /* Profile.xcconfig */; + baseConfigurationReference = D0A463DB17E57C45000F5021 /* Test.xcconfig */; buildSettings = { - GCC_STRICT_ALIASING = NO; + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; HEADER_SEARCH_PATHS = ( External/libgit2/include, /usr/local/include, ); - IPHONEOS_DEPLOYMENT_TARGET = 6.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LIBRARY_SEARCH_PATHS = ( ., External, ); - MACOSX_DEPLOYMENT_TARGET = 10.7; + MACOSX_DEPLOYMENT_TARGET = 10.10; OTHER_CFLAGS = ( "$(inherited)", "-DGIT_SSH", ); - TEST_AFTER_BUILD = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-incomplete-umbrella", + ); }; - name = Profile; + name = Test; }; - D03FC3D81602E97F00BCFA73 /* Profile */ = { + D019778E19F830F500F523DA /* Test */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D8186A174421EB00995A2E /* Mac-Framework.xcconfig */; + baseConfigurationReference = 4DB97075264596AE00D14944 /* macOS-Framework.xcconfig */; buildSettings = { DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; - GCC_PREFIX_HEADER = ObjectiveGitFramework_Prefix.pch; INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/opt/openssl/lib, + "$(PROJECT_DIR)/External/build/lib", + "$(PROJECT_DIR)/External", + ); + MODULEMAP_FILE = ObjectiveGit.modulemap; OTHER_LDFLAGS = ( - "-lgit2", "-force_load", - External/libgit2.a, + "External/libgit2-mac.a", /usr/local/lib/libssh2.a, "-lcrypto", "-lssl", + "-lcurl", ); + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = ObjectiveGit; - USER_HEADER_SEARCH_PATHS = ""; WRAPPER_EXTENSION = framework; }; - name = Profile; + name = Test; + }; + D019778F19F830F500F523DA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4DB97078264596AE00D14944 /* macOS-XCTest.xcconfig */; + buildSettings = { + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); + HEADER_SEARCH_PATHS = ( + External/libgit2/include, + /usr/local/include, + ); + INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Test; + }; + D019779019F830F500F523DA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D019778C19F830D100F523DA /* iOS-Framework.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + External/build/include, + ); + INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/External/build/lib"; + MODULEMAP_FILE = ObjectiveGit.modulemap; + OTHER_LDFLAGS = "-all_load"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = ObjectiveGit; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/libgit2/include"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Test; }; - D03FC3D91602E97F00BCFA73 /* Profile */ = { + D019779219F830F500F523DA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4DB97076264596AE00D14944 /* macOS-StaticLibrary.xcconfig */; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Test; + }; + D019779319F830F500F523DA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0D81865174421EB00995A2E /* iOS-StaticLibrary.xcconfig */; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Test; + }; + D019779419F830F500F523DA /* Test */ = { isa = XCBuildConfiguration; baseConfigurationReference = D0D81865174421EB00995A2E /* iOS-StaticLibrary.xcconfig */; buildSettings = { - GCC_PREFIX_HEADER = ObjectiveGitFramework_Prefix.pch; + PRODUCT_NAME = "OpenSSL-iOS"; + }; + name = Test; + }; + D019779519F830F500F523DA /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0D81865174421EB00995A2E /* iOS-StaticLibrary.xcconfig */; + buildSettings = { + PRODUCT_NAME = "libssh2-iOS"; + }; + name = Test; + }; + D01B6EE719F82E2000D411BC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D019778C19F830D100F523DA /* iOS-Framework.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + External/build/include, + ); + INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/External/build/lib"; + MODULEMAP_FILE = ObjectiveGit.modulemap; + OTHER_LDFLAGS = "-all_load"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = ObjectiveGit; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/libgit2/include"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + D01B6EE819F82E2000D411BC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D019778C19F830D100F523DA /* iOS-Framework.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + External/build/include, + ); + INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/External/build/lib"; + MODULEMAP_FILE = ObjectiveGit.modulemap; + OTHER_LDFLAGS = "-all_load"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = ObjectiveGit; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/libgit2/include"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D01B6EE919F82E2000D411BC /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D019778C19F830D100F523DA /* iOS-Framework.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + External/build/include, + ); + INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/External/build/lib"; + MODULEMAP_FILE = ObjectiveGit.modulemap; + OTHER_LDFLAGS = "-all_load"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = ObjectiveGit; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/External/libgit2/include"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Profile; + }; + D03FC3D71602E97F00BCFA73 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0A463D917E57C45000F5021 /* Profile.xcconfig */; + buildSettings = { + CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; HEADER_SEARCH_PATHS = ( External/libgit2/include, - "$(OBJROOT)/UninstalledProducts/include", - "External/libssh2-ios/include/libssh2", + /usr/local/include, ); + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LIBRARY_SEARCH_PATHS = ( - "External/ios-openssl/lib", - "External/libssh2-ios/lib", - "$(inherited)", + ., + External, ); - ONLY_ACTIVE_ARCH = NO; + MACOSX_DEPLOYMENT_TARGET = 10.10; OTHER_CFLAGS = ( "$(inherited)", "-DGIT_SSH", ); + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + WARNING_CFLAGS = ( + "$(inherited)", + "-Wno-incomplete-umbrella", + ); + }; + name = Profile; + }; + D03FC3D81602E97F00BCFA73 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4DB97075264596AE00D14944 /* macOS-Framework.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Info.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/opt/openssl/lib, + "$(PROJECT_DIR)/External/build/lib", + "$(PROJECT_DIR)/External", + ); + MODULEMAP_FILE = ObjectiveGit.modulemap; OTHER_LDFLAGS = ( - "-lgit2-ios", - "-all_load", - "-lssh2-ios", + "-force_load", + "External/libgit2-mac.a", + /usr/local/lib/libssh2.a, + "-lcrypto", + "-lssl", + "-lcurl", ); - PRIVATE_HEADERS_FOLDER_PATH = include/ObjectiveGit; - PRODUCT_NAME = "ObjectiveGit-iOS"; - PUBLIC_HEADERS_FOLDER_PATH = include/ObjectiveGit; - USER_HEADER_SEARCH_PATHS = ""; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = ObjectiveGit; + WRAPPER_EXTENSION = framework; }; name = Profile; }; D03FC3DA1602E97F00BCFA73 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D81867174421EB00995A2E /* Mac-Application.xcconfig */; + baseConfigurationReference = 4DB97078264596AE00D14944 /* macOS-XCTest.xcconfig */; buildSettings = { - FRAMEWORK_SEARCH_PATHS = "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\""; - GCC_PREFIX_HEADER = "ObjectiveGitTests/ObjectiveGitTests-Prefix.pch"; - HEADER_SEARCH_PATHS = ( + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - ObjectiveGitTests/specta/src, - "ObjectiveGitTests/expecta/src/**", + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); + HEADER_SEARCH_PATHS = ( + External/libgit2/include, + /usr/local/include, ); INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; - OTHER_LDFLAGS = "-all_load"; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = octest; }; name = Profile; }; D03FC3DB1602E97F00BCFA73 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D8186B174421EB00995A2E /* Mac-StaticLibrary.xcconfig */; + baseConfigurationReference = 4DB97076264596AE00D14944 /* macOS-StaticLibrary.xcconfig */; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -1782,7 +2704,7 @@ }; D0A330EF16027F1E00A616FA /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D8186B174421EB00995A2E /* Mac-StaticLibrary.xcconfig */; + baseConfigurationReference = 4DB97076264596AE00D14944 /* macOS-StaticLibrary.xcconfig */; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -1790,7 +2712,7 @@ }; D0A330F016027F1E00A616FA /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0D8186B174421EB00995A2E /* Mac-StaticLibrary.xcconfig */; + baseConfigurationReference = 4DB97076264596AE00D14944 /* macOS-StaticLibrary.xcconfig */; buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -1812,23 +2734,235 @@ }; name = Release; }; + F879D8401B4B7F7D002D5C07 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0A463DB17E57C45000F5021 /* Test.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "NOCRYPT=1", + "NOUNCRYPT=1", + "NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER=0", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + ., + External, + ); + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + F879D8411B4B7F7D002D5C07 /* Test */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0A463DB17E57C45000F5021 /* Test.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + ., + External, + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Test; + }; + F879D8421B4B7F7D002D5C07 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0A463DB17E57C45000F5021 /* Test.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + ., + External, + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F879D8431B4B7F7D002D5C07 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D0A463DB17E57C45000F5021 /* Test.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = NO; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(inherited)"; + INFOPLIST_FILE = "ObjectiveGitTests/ObjectiveGitTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + ., + External, + ); + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "org.libgit2.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 04DB464B133AB58900D9C624 /* Build configuration list for PBXNativeTarget "ObjectiveGit-iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 04DB4646133AB57600D9C624 /* Debug */, - 04DB4647133AB57600D9C624 /* Release */, - D03FC3D91602E97F00BCFA73 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "ObjectiveGit" */ = { + 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "ObjectiveGit-Mac" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91AE08733DA50010E9CD /* Debug */, + D019778E19F830F500F523DA /* Test */, 1DEB91AF08733DA50010E9CD /* Release */, D03FC3D81602E97F00BCFA73 /* Profile */, ); @@ -1839,6 +2973,7 @@ isa = XCConfigurationList; buildConfigurations = ( 1DEB91B208733DA50010E9CD /* Debug */, + D019778D19F830F500F523DA /* Test */, 1DEB91B308733DA50010E9CD /* Release */, D03FC3D71602E97F00BCFA73 /* Profile */, ); @@ -1849,6 +2984,7 @@ isa = XCConfigurationList; buildConfigurations = ( 6A28265517C69CB400C6A948 /* Debug */, + D019779419F830F500F523DA /* Test */, 6A28265617C69CB400C6A948 /* Release */, 6A28265717C69CB400C6A948 /* Profile */, ); @@ -1859,26 +2995,40 @@ isa = XCConfigurationList; buildConfigurations = ( 6A3C609317D5963700382DFF /* Debug */, + D019779519F830F500F523DA /* Test */, 6A3C609417D5963700382DFF /* Release */, 6A3C609517D5963700382DFF /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 88F05A7F16011E5400B7AD1D /* Build configuration list for PBXNativeTarget "ObjectiveGitTests" */ = { + 88F05A7F16011E5400B7AD1D /* Build configuration list for PBXNativeTarget "ObjectiveGit-MacTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 88F05A8016011E5400B7AD1D /* Debug */, + D019778F19F830F500F523DA /* Test */, 88F05A8116011E5400B7AD1D /* Release */, D03FC3DA1602E97F00BCFA73 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D01B6EE619F82E2000D411BC /* Build configuration list for PBXNativeTarget "ObjectiveGit-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D01B6EE719F82E2000D411BC /* Debug */, + D019779019F830F500F523DA /* Test */, + D01B6EE819F82E2000D411BC /* Release */, + D01B6EE919F82E2000D411BC /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; D0A330EE16027F1E00A616FA /* Build configuration list for PBXAggregateTarget "libgit2" */ = { isa = XCConfigurationList; buildConfigurations = ( D0A330EF16027F1E00A616FA /* Debug */, + D019779219F830F500F523DA /* Test */, D0A330F016027F1E00A616FA /* Release */, D03FC3DB1602E97F00BCFA73 /* Profile */, ); @@ -1889,12 +3039,24 @@ isa = XCConfigurationList; buildConfigurations = ( D0A330F416027F3700A616FA /* Debug */, + D019779319F830F500F523DA /* Test */, D0A330F516027F3700A616FA /* Release */, D03FC3DC1602E97F00BCFA73 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F879D83F1B4B7F7D002D5C07 /* Build configuration list for PBXNativeTarget "ObjectiveGit-iOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F879D8401B4B7F7D002D5C07 /* Debug */, + F879D8411B4B7F7D002D5C07 /* Test */, + F879D8421B4B7F7D002D5C07 /* Release */, + F879D8431B4B7F7D002D5C07 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 0867D690FE84028FC02AAC07 /* Project object */; diff --git a/ObjectiveGitFramework.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ObjectiveGitFramework.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..f3c74e883 --- /dev/null +++ b/ObjectiveGitFramework.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ObjectiveGitFramework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ObjectiveGitFramework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/ObjectiveGitFramework.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit Mac.xcscheme b/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit Mac.xcscheme new file mode 100644 index 000000000..02ffc2cf1 --- /dev/null +++ b/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit Mac.xcscheme @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit iOS.xcscheme b/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit iOS.xcscheme new file mode 100644 index 000000000..f474d5e83 --- /dev/null +++ b/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit iOS.xcscheme @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit-iOS.xcscheme b/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit-iOS.xcscheme deleted file mode 100644 index 1833b790d..000000000 --- a/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit-iOS.xcscheme +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit.xcscheme b/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit.xcscheme deleted file mode 100644 index 4d2bba870..000000000 --- a/ObjectiveGitFramework.xcodeproj/xcshareddata/xcschemes/ObjectiveGit.xcscheme +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ObjectiveGitFramework.xcworkspace/contents.xcworkspacedata b/ObjectiveGitFramework.xcworkspace/contents.xcworkspacedata index 288e739a2..e759ae56c 100644 --- a/ObjectiveGitFramework.xcworkspace/contents.xcworkspacedata +++ b/ObjectiveGitFramework.xcworkspace/contents.xcworkspacedata @@ -5,9 +5,6 @@ location = "group:ObjectiveGitFramework.xcodeproj"> - - + location = "group:../libgit2"> diff --git a/ObjectiveGitFramework.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ObjectiveGitFramework.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/ObjectiveGitFramework.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ObjectiveGitFramework.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ObjectiveGitFramework.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..0c67376eb --- /dev/null +++ b/ObjectiveGitFramework.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/ObjectiveGitFramework_Prefix.pch b/ObjectiveGitFramework_Prefix.pch deleted file mode 100644 index 26cd31d46..000000000 --- a/ObjectiveGitFramework_Prefix.pch +++ /dev/null @@ -1,9 +0,0 @@ -// -// Prefix header for all source files of the 'ObjectiveGitFramework' target in the 'ObjectiveGitFramework' project. -// - -#ifdef __OBJC__ - #import -#endif - -#define GTLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); diff --git a/ObjectiveGitTests/GTBlameSpec.m b/ObjectiveGitTests/GTBlameSpec.m index 8ad885580..2cbeec1a4 100644 --- a/ObjectiveGitTests/GTBlameSpec.m +++ b/ObjectiveGitTests/GTBlameSpec.m @@ -6,32 +6,38 @@ // Copyright (c) 2014 GitHub, Inc. All rights reserved. // -SpecBegin(GTBlame) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTBlameSpec) __block GTBlame *blame = nil; beforeEach(^{ blame = [self.testAppFixtureRepository blameWithFile:@"README1.txt" options:nil error:nil]; - expect(blame).toNot.beNil(); + expect(blame).notTo(beNil()); }); it(@"can count the hunks", ^{ - expect(blame.hunkCount).to.equal(4); + expect(@(blame.hunkCount)).to(equal(@4)); }); it(@"can read hunk properties", ^{ GTBlameHunk *hunk = [blame hunkAtIndex:1]; - - expect(hunk).notTo.beNil(); - expect(NSEqualRanges(hunk.lines, NSMakeRange(22, 1))).to.beTruthy(); - expect(hunk.finalCommitOID.SHA).to.equal(@"82dc47f6ba3beecab33080a1136d8913098e1801"); - expect(hunk.finalSignature).toNot.beNil(); - expect(hunk.originalPath).to.equal(@"README1.txt"); - expect(hunk.isBoundary).to.beFalsy(); + + expect(hunk).notTo(beNil()); + expect(@(NSEqualRanges(hunk.lines, NSMakeRange(22, 1)))).to(beTruthy()); + expect(hunk.finalCommitOID.SHA).to(equal(@"82dc47f6ba3beecab33080a1136d8913098e1801")); + expect(hunk.finalSignature).notTo(beNil()); + expect(hunk.originalPath).to(equal(@"README1.txt")); + expect(@(hunk.isBoundary)).to(beFalsy()); }); it(@"The number of hunks in the `hunks` array should match `hunkCount`", ^{ - expect(blame.hunks).to.haveCountOf(blame.hunkCount); + expect(@(blame.hunks.count)).to(equal(@(blame.hunkCount))); }); it(@"should be able to enumerate all the hunks in a blame, stopping when instructed", ^{ @@ -40,42 +46,46 @@ [mutableArray addObject:hunk]; *stop = YES; }]; - - expect(mutableArray).to.haveCountOf(1); + + expect(@(mutableArray.count)).to(equal(@(1))); }); it(@"should be able to get the same hunk from an index or a line", ^{ GTBlameHunk *hunk = [blame hunkAtIndex:0]; GTBlameHunk *lineHunk = [blame hunkAtLineNumber:1]; - expect(hunk).to.equal(lineHunk); + expect(hunk).to(equal(lineHunk)); }); describe(@"Creating a blame with options", ^{ it(@"should follow the instructions provided by the GTBlameOptionsOldestCommitOID key", ^{ GTBlame *optionsBlame = [self.testAppFixtureRepository blameWithFile:@"README1.txt" options:@{ GTBlameOptionsOldestCommitOID: [GTOID oidWithSHA:@"1d69f3c0aeaf0d62e25591987b93b8ffc53abd77"] } error:nil]; - expect(optionsBlame).toNot.beNil(); - expect(optionsBlame).notTo.equal(blame); + expect(optionsBlame).notTo(beNil()); + expect(optionsBlame).notTo(equal(blame)); }); - + it(@"should follow the instructions provided by the GTBlameOptionsNewestCommitOID key", ^{ GTOID *newOID = [GTOID oidWithSHA:@"6317779b4731d9c837dcc6972b964bdf4211eeef"]; GTBlame *optionsBlame = [self.testAppFixtureRepository blameWithFile:@"README1.txt" options:@{ GTBlameOptionsNewestCommitOID: newOID } error:nil]; GTBlameHunk *hunk = [optionsBlame hunkAtIndex:0]; - expect(hunk.lines.location).to.equal(1); - expect(hunk.lines.length).to.equal(25); + expect(@(hunk.lines.location)).to(equal(@1)); + expect(@(hunk.lines.length)).to(equal(@25)); }); - + it(@"should follow the instructions provided by GTBlameOptionsFirstLine and GTBlameOptionsLastLine keys", ^{ GTBlame *optionsBlame = [self.testAppFixtureRepository blameWithFile:@"README1.txt" options:@{ GTBlameOptionsFirstLine: @22, GTBlameOptionsLastLine: @24 } error:nil]; GTBlameHunk *hunk = [optionsBlame hunkAtIndex:0]; - - expect(optionsBlame).toNot.beNil(); - expect(hunk.lines.location).to.equal(22); - expect(hunk.lines.length).to.equal(1); + + expect(optionsBlame).notTo(beNil()); + expect(@(hunk.lines.location)).to(equal(@22)); + expect(@(hunk.lines.length)).to(equal(@1)); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTBlobSpec.m b/ObjectiveGitTests/GTBlobSpec.m index 22fc778c2..97e735337 100644 --- a/ObjectiveGitTests/GTBlobSpec.m +++ b/ObjectiveGitTests/GTBlobSpec.m @@ -6,9 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTBlob.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTBlob) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTBlobSpec) __block GTRepository *repository; __block NSString *blobSHA; @@ -19,23 +23,23 @@ repository = self.bareFixtureRepository; blobSHA = @"fa49b077972391ad58037050f2a75f74e3671e92"; blob = [repository lookUpObjectBySHA:blobSHA objectType:GTObjectTypeBlob error:NULL]; - expect(blob).notTo.beNil(); + expect(blob).notTo(beNil()); }); it(@"has a size", ^{ - expect(blob.size).to.equal(9); + expect(@(blob.size)).to(equal(@9)); }); it(@"has content", ^{ - expect(blob.content).to.equal(@"new file\n"); + expect(blob.content).to(equal(@"new file\n")); }); it(@"has type", ^{ - expect(blob.type).to.equal(@"blob"); + expect(blob.type).to(equal(@"blob")); }); it(@"has a SHA", ^{ - expect(blob.SHA).to.equal(blobSHA); + expect(blob.SHA).to(equal(blobSHA)); }); }); @@ -48,9 +52,9 @@ it(@"works with valid parameters", ^{ NSError *error = nil; blob = [GTBlob blobWithString:@"a new blob content" inRepository:repository error:&error]; - expect(error).to.beNil(); - expect(blob).notTo.beNil(); - expect(blob.SHA).notTo.beNil(); + expect(error).to(beNil()); + expect(blob).notTo(beNil()); + expect(blob.SHA).notTo(beNil()); }); }); @@ -61,9 +65,9 @@ NSError *error = nil; blob = [GTBlob blobWithData:content inRepository:repository error:&error]; - expect(error).to.beNil(); - expect(blob).notTo.beNil(); - expect(blob.SHA).notTo.beNil(); + expect(error).to(beNil()); + expect(blob).notTo(beNil()); + expect(blob.SHA).notTo(beNil()); }); }); @@ -75,16 +79,20 @@ NSError *error = nil; BOOL success = [fileContent writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:&error]; - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); blob = [GTBlob blobWithFile:fileURL inRepository:repository error:&error]; - expect(error).to.beNil(); - expect(blob).notTo.beNil(); - expect(blob.SHA).notTo.beNil(); - expect(blob.content).to.equal(fileContent); + expect(error).to(beNil()); + expect(blob).notTo(beNil()); + expect(blob.SHA).notTo(beNil()); + expect(blob.content).to(equal(fileContent)); }); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTBranchSpec.m b/ObjectiveGitTests/GTBranchSpec.m index 433c16c83..2b997e222 100644 --- a/ObjectiveGitTests/GTBranchSpec.m +++ b/ObjectiveGitTests/GTBranchSpec.m @@ -6,9 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTBranch.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTBranch) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTBranchSpec) __block GTRepository *repository; __block GTBranch *masterBranch; @@ -16,47 +20,57 @@ beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); NSError *error = nil; masterBranch = [repository currentBranchWithError:&error]; - expect(masterBranch).notTo.beNil(); - expect(error).to.beNil(); + expect(masterBranch).notTo(beNil()); + expect(error).to(beNil()); BOOL success = NO; trackingBranch = [masterBranch trackingBranchWithError:&error success:&success]; - expect(trackingBranch).notTo.equal(masterBranch); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(trackingBranch).notTo(equal(masterBranch)); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); +}); + +describe(@"name", ^{ + it(@"should use just the branch name for a local branch", ^{ + expect(masterBranch.name).to(equal(@"master")); + }); + + it(@"should include the remote name for a tracking branch", ^{ + expect(trackingBranch.name).to(equal(@"origin/master")); + }); }); describe(@"shortName", ^{ it(@"should use just the branch name for a local branch", ^{ - expect(masterBranch.shortName).to.equal(@"master"); + expect(masterBranch.shortName).to(equal(@"master")); }); it(@"should not include the remote name for a tracking branch", ^{ - expect(trackingBranch.shortName).to.equal(@"master"); + expect(trackingBranch.shortName).to(equal(@"master")); }); }); describe(@"remoteName", ^{ it(@"should return nil for a local branch", ^{ - expect(masterBranch.remoteName).to.beNil(); + expect(masterBranch.remoteName).to(beNil()); }); it(@"should return the remote name for a tracking branch", ^{ - expect(trackingBranch.remoteName).to.equal(@"origin"); + expect(trackingBranch.remoteName).to(equal(@"origin")); }); }); describe(@"branchType", ^{ it(@"should be GTBranchTypeLocal for a local branch", ^{ - expect(masterBranch.branchType).to.equal(GTBranchTypeLocal); + expect(@(masterBranch.branchType)).to(equal(@(GTBranchTypeLocal))); }); it(@"should be GTBranchTypeRemote for a tracking branch", ^{ - expect(trackingBranch.branchType).to.equal(GTBranchTypeRemote); + expect(@(trackingBranch.branchType)).to(equal(@(GTBranchTypeRemote))); }); }); @@ -65,16 +79,16 @@ size_t ahead = 0; size_t behind = 0; [masterBranch calculateAhead:&ahead behind:&behind relativeTo:trackingBranch error:NULL]; - expect(ahead).to.equal(9); - expect(behind).to.equal(0); + expect(@(ahead)).to(equal(@9)); + expect(@(behind)).to(equal(@0)); }); it(@"should calculate ahead/behind relative to the local branch", ^{ size_t ahead = 0; size_t behind = 0; [trackingBranch calculateAhead:&ahead behind:&behind relativeTo:masterBranch error:NULL]; - expect(ahead).to.equal(0); - expect(behind).to.equal(9); + expect(@(ahead)).to(equal(@0)); + expect(@(behind)).to(equal(@9)); }); }); @@ -82,8 +96,8 @@ it(@"should return unique commits relative to the tracking branch", ^{ NSError *error = nil; NSArray *commits = [masterBranch uniqueCommitsRelativeToBranch:trackingBranch error:&error]; - expect(commits).notTo.beNil(); - expect(error).to.beNil(); + expect(commits).notTo(beNil()); + expect(error).to(beNil()); NSMutableArray *SHAs = [NSMutableArray array]; for (GTCommit *commit in commits) { @@ -102,14 +116,14 @@ @"9f90c6e24629fae3ef51101bb6448342b44098ef", ]; - expect(SHAs).to.equal(expectedSHAs); + expect(SHAs).to(equal(expectedSHAs)); }); it(@"should return no unique commits relative to the local branch", ^{ NSError *error = nil; NSArray *commits = [trackingBranch uniqueCommitsRelativeToBranch:masterBranch error:&error]; - expect(commits).to.equal(@[]); - expect(error).to.beNil(); + expect(commits).to(equal(@[])); + expect(error).to(beNil()); }); }); @@ -117,13 +131,13 @@ it(@"should reload the branch from disk", ^{ static NSString * const originalSHA = @"a4bca6b67a5483169963572ee3da563da33712f7"; static NSString * const updatedSHA = @"6b0c1c8b8816416089c534e474f4c692a76ac14f"; - expect([masterBranch targetCommitAndReturnError:NULL].SHA).to.equal(originalSHA); - [masterBranch.reference referenceByUpdatingTarget:updatedSHA committer:nil message:nil error:NULL]; + expect([masterBranch targetCommitWithError:NULL].SHA).to(equal(originalSHA)); + [masterBranch.reference referenceByUpdatingTarget:updatedSHA message:nil error:NULL]; GTBranch *reloadedBranch = [masterBranch reloadedBranchWithError:NULL]; - expect(reloadedBranch).notTo.beNil(); - expect([reloadedBranch targetCommitAndReturnError:NULL].SHA).to.equal(updatedSHA); - expect([masterBranch targetCommitAndReturnError:NULL].SHA).to.equal(originalSHA); + expect(reloadedBranch).notTo(beNil()); + expect([reloadedBranch targetCommitWithError:NULL].SHA).to(equal(updatedSHA)); + expect([masterBranch targetCommitWithError:NULL].SHA).to(equal(originalSHA)); }); }); @@ -131,8 +145,8 @@ it(@"should return the count of commits in the branch", ^{ NSError *error = nil; NSUInteger commitCount = [masterBranch numberOfCommitsWithError:&error]; - expect(commitCount).to.equal(164); - expect(error).to.beNil(); + expect(@(commitCount)).to(equal(@164)); + expect(error).to(beNil()); }); }); @@ -140,72 +154,160 @@ it(@"should return the tracking branch for a local branch that tracks a remote branch", ^{ NSError *error = nil; GTBranch *masterBranch = [repository lookUpBranchWithName:@"master" type:GTBranchTypeLocal success:NULL error:&error]; - expect(masterBranch).notTo.beNil(); - expect(error).to.beNil(); + expect(masterBranch).notTo(beNil()); + expect(error).to(beNil()); BOOL success = NO; GTBranch *trackingBranch = [masterBranch trackingBranchWithError:&error success:&success]; - expect(trackingBranch).notTo.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(trackingBranch).notTo(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); it(@"should return nil for a local branch that doesn't track a remote branch", ^{ GTOID *OID = [[GTOID alloc] initWithSHA:@"6b0c1c8b8816416089c534e474f4c692a76ac14f"]; NSError *error = nil; - GTReference *otherRef = [repository createReferenceNamed:@"refs/heads/yet-another-branch" fromOID:OID committer:nil message:nil error:&error]; - expect(otherRef).notTo.beNil(); - expect(error).to.beNil(); + GTReference *otherRef = [repository createReferenceNamed:@"refs/heads/yet-another-branch" fromOID:OID message:nil error:&error]; + expect(otherRef).notTo(beNil()); + expect(error).to(beNil()); - GTBranch *otherBranch = [GTBranch branchWithReference:otherRef repository:repository]; - expect(otherBranch).notTo.beNil(); + GTBranch *otherBranch = [GTBranch branchWithReference:otherRef]; + expect(otherBranch).notTo(beNil()); BOOL success = NO; trackingBranch = [otherBranch trackingBranchWithError:&error success:&success]; - expect(trackingBranch).to.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(trackingBranch).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); it(@"should return itself for a remote branch", ^{ NSError *error = nil; - GTReference *remoteRef = [GTReference referenceByLookingUpReferencedNamed:@"refs/remotes/origin/master" inRepository:repository error:&error]; - expect(remoteRef).notTo.beNil(); - expect(error).to.beNil(); + GTReference *remoteRef = [repository lookUpReferenceWithName:@"refs/remotes/origin/master" error:&error]; + expect(remoteRef).notTo(beNil()); + expect(error).to(beNil()); - GTBranch *remoteBranch = [GTBranch branchWithReference:remoteRef repository:repository]; - expect(remoteBranch).notTo.beNil(); + GTBranch *remoteBranch = [GTBranch branchWithReference:remoteRef]; + expect(remoteBranch).notTo(beNil()); BOOL success = NO; GTBranch *remoteTrackingBranch = [remoteBranch trackingBranchWithError:&error success:&success]; - expect(remoteTrackingBranch).to.equal(remoteBranch); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(remoteTrackingBranch).to(equal(remoteBranch)); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); }); -// TODO: Test branch renaming, branch upstream -//- (void)testCanRenameBranch { -// -// NSError *error = nil; -// GTRepository *repo = [GTRepository repoByOpeningRepositoryInDirectory:[NSURL URLWithString:TEST_REPO_PATH()] error:&error]; -// STAssertNil(error, [error localizedDescription]); -// -// NSArray *branches = [GTBranch listAllLocalBranchesInRepository:repo error:&error]; -// STAssertNotNil(branches, [error localizedDescription], nil); -// STAssertEquals(2, (int)branches.count, nil); -// -// NSString *newBranchName = [NSString stringWithFormat:@"%@%@", [GTBranch localNamePrefix], @"this_is_the_renamed_branch"]; -// GTBranch *firstBranch = [branches objectAtIndex:0]; -// NSString *originalBranchName = firstBranch.name; -// BOOL success = [firstBranch.reference setName:newBranchName error:&error]; -// STAssertTrue(success, [error localizedDescription]); -// STAssertEqualObjects(firstBranch.name, newBranchName, nil); -// -// success = [firstBranch.reference setName:originalBranchName error:&error]; -// STAssertTrue(success, [error localizedDescription]); -// STAssertEqualObjects(firstBranch.name, originalBranchName, nil); -//} +describe(@"-updateTrackingBranch:error:", ^{ + __block GTBranch *masterBranch; + beforeEach(^{ + masterBranch = [repository lookUpBranchWithName:@"master" type:GTBranchTypeLocal success:NULL error:NULL]; + expect(masterBranch).notTo(beNil()); + }); + + it(@"should set a tracking branch", ^{ + GTBranch *branch = [repository lookUpBranchWithName:@"feature" type:GTBranchTypeLocal success:NULL error:NULL]; + expect(branch).notTo(beNil()); + + BOOL success = NO; + GTBranch *trackingBranch = [branch trackingBranchWithError:NULL success:&success]; + expect(trackingBranch).to(beNil()); + expect(@(success)).to(beTruthy()); + + NSError *error; + success = [branch updateTrackingBranch:masterBranch error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + trackingBranch = [branch trackingBranchWithError:NULL success:&success]; + expect(trackingBranch).notTo(beNil()); + expect(@(success)).to(beTruthy()); + }); + + it(@"should unset a tracking branch", ^{ + BOOL success = NO; + GTBranch *trackingBranch = [masterBranch trackingBranchWithError:NULL success:&success]; + expect(trackingBranch).notTo(beNil()); + expect(@(success)).to(beTruthy()); + + success = [masterBranch updateTrackingBranch:nil error:NULL]; + expect(@(success)).to(beTruthy()); + + trackingBranch = [masterBranch trackingBranchWithError:NULL success:&success]; + expect(trackingBranch).to(beNil()); + expect(@(success)).to(beTruthy()); + }); + + it(@"should set a remote tracking branch without branches amount change", ^{ + GTRepository *repository = self.testAppForkFixtureRepository; + expect(repository).notTo(beNil()); + + NSError *error = nil; + BOOL success = NO; + GTBranch *remoteBranch = [repository lookUpBranchWithName:@"github/BranchC" type:GTBranchTypeRemote success:&success error:&error]; + expect(remoteBranch).notTo(beNil()); + expect(error).to(beNil()); + + NSArray *beforeBranches = [repository branches:&error]; + expect(error).to(beNil()); + + GTBranch *localBranch = [repository createBranchNamed:remoteBranch.shortName fromOID:remoteBranch.OID message:nil error:&error]; + expect(localBranch).notTo(beNil()); + expect(error).to(beNil()); + + NSArray *inBranches = [repository branches:&error]; + expect(error).to(beNil()); + + [localBranch updateTrackingBranch:remoteBranch error:&error]; + expect(error).to(beNil()); + + NSArray *afterBranches = [repository branches:&error]; + expect(error).to(beNil()); + + expect(@(beforeBranches.count + 1)).to(equal(@(inBranches.count))); + expect(@(beforeBranches.count)).to(equal(@(afterBranches.count))); + + }); +}); + +describe(@"-rename:force:error", ^{ + __block GTBranch *masterBranch; + beforeEach(^{ + masterBranch = [repository lookUpBranchWithName:@"master" type:GTBranchTypeLocal success:NULL error:NULL]; + expect(masterBranch).notTo(beNil()); + }); + + it(@"should rename the branch", ^{ + NSError *error = nil; + BOOL success = [masterBranch rename:@"plop" force:NO error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + expect(masterBranch.shortName).to(equal(@"plop")); + }); + + it(@"should fail on duplicates", ^{ + NSError *error = nil; + BOOL success = [masterBranch rename:@"feature" force:NO error:&error]; + expect(@(success)).to(beFalsy()); + expect(error).notTo(beNil()); + + expect(masterBranch.shortName).to(equal(@"master")); + }); + + it(@"should rename when forced", ^{ + NSError *error = nil; + BOOL success = [masterBranch rename:@"feature" force:YES error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + expect(masterBranch.shortName).to(equal(@"feature")); + }); +}); + +afterEach(^{ + [self tearDown]; +}); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTCommitSpec.m b/ObjectiveGitTests/GTCommitSpec.m index 1e6e6e977..38ebe87ff 100644 --- a/ObjectiveGitTests/GTCommitSpec.m +++ b/ObjectiveGitTests/GTCommitSpec.m @@ -6,9 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTCommit.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTCommit) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTCommitSpec) __block GTRepository *repository; @@ -21,42 +25,63 @@ NSString *commitSHA = @"8496071c1b46c854b31185ea97743be6a8774479"; GTCommit *commit = [repository lookUpObjectBySHA:commitSHA error:&error]; - expect(commit).notTo.beNil(); - expect(error).to.beNil(); + expect(commit).notTo(beNil()); + expect(error).to(beNil()); - expect(commit).to.beInstanceOf(GTCommit.class); - expect(commit.type).to.equal(@"commit"); - expect(commit.SHA).to.equal(commitSHA); + expect(commit).to(beAnInstanceOf(GTCommit.class)); + expect(commit.type).to(equal(@"commit")); + expect(commit.SHA).to(equal(commitSHA)); + expect(commit.OID).to(equal([GTOID oidWithSHA:commitSHA])); - expect(commit.message).to.equal(@"testing\n"); - expect(commit.messageSummary).to.equal(@"testing"); - expect(commit.messageDetails).to.equal(@""); - expect(commit.commitDate).to.equal([NSDate dateWithTimeIntervalSince1970:1273360386]); + expect(commit.message).to(equal(@"testing\n")); + expect(commit.messageSummary).to(equal(@"testing")); + expect(commit.messageDetails).to(equal(@"")); + expect(commit.commitDate).to(equal([NSDate dateWithTimeIntervalSince1970:1273360386])); GTSignature *author = commit.author; - expect(author).notTo.beNil(); - expect(author.name).to.equal(@"Scott Chacon"); - expect(author.email).to.equal(@"schacon@gmail.com"); - expect(author.time).to.equal([NSDate dateWithTimeIntervalSince1970:1273360386]); + expect(author).notTo(beNil()); + expect(author.name).to(equal(@"Scott Chacon")); + expect(author.email).to(equal(@"schacon@gmail.com")); + expect(author.time).to(equal([NSDate dateWithTimeIntervalSince1970:1273360386])); GTSignature *committer = commit.committer; - expect(committer).notTo.beNil(); - expect(committer.name).to.equal(@"Scott Chacon"); - expect(committer.email).to.equal(@"schacon@gmail.com"); - expect(committer.time).to.equal([NSDate dateWithTimeIntervalSince1970:1273360386]); + expect(committer).notTo(beNil()); + expect(committer.name).to(equal(@"Scott Chacon")); + expect(committer.email).to(equal(@"schacon@gmail.com")); + expect(committer.time).to(equal([NSDate dateWithTimeIntervalSince1970:1273360386])); - expect(commit.tree.SHA).to.equal(@"181037049a54a1eb5fab404658a3a250b44335d7"); - expect(commit.parents.count).to.equal(0); + expect(commit.tree.SHA).to(equal(@"181037049a54a1eb5fab404658a3a250b44335d7")); + expect(@(commit.parents.count)).to(equal(@0)); }); it(@"can have multiple parents", ^{ NSError *error = nil; NSString *commitSHA = @"a4a7dce85cf63874e984719f4fdd239f5145052f"; GTCommit *commit = [repository lookUpObjectBySHA:commitSHA error:&error]; - expect(commit).notTo.beNil(); - expect(error).to.beNil(); + expect(commit).notTo(beNil()); + expect(error).to(beNil()); + + NSArray *commitOIDs = @[@"c47800c7266a2be04c571c04d5a6614691ea99bd", @"9fd738e8f7967c078dceed8190330fc8648ee56a"]; + NSArray *commitParents = commit.parentOIDs; + expect(@(commitParents.count)).to(equal(@(commitOIDs.count))); + expect([commitParents valueForKey:@"SHA"]).to(equal(commitOIDs)); + + commitParents = commit.parents; + expect(@(commitParents.count)).to(equal(@(commitOIDs.count))); + expect([commitParents valueForKeyPath:@"OID.SHA"]).to(equal(commitOIDs)); +}); + +it(@"can identify merges", ^{ + NSError *error; + NSString *commitSHA = @"a4a7dce85cf63874e984719f4fdd239f5145052f"; + GTCommit *commit = [repository lookUpObjectBySHA:commitSHA error:&error]; + expect(commit).notTo(beNil()); + + expect(@(commit.merge)).to(beTruthy()); +}); - expect(commit.parents.count).to.equal(2); +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTConfigurationSpec.m b/ObjectiveGitTests/GTConfigurationSpec.m index ffdb81fed..25f3c1248 100644 --- a/ObjectiveGitTests/GTConfigurationSpec.m +++ b/ObjectiveGitTests/GTConfigurationSpec.m @@ -6,9 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTConfiguration.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTConfiguration) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTConfigurationSpec) describe(@"+defaultConfiguration", ^{ static NSString * const testKey = @"universe.answer"; @@ -18,33 +22,37 @@ beforeEach(^{ config = [GTConfiguration defaultConfiguration]; - expect(config).notTo.beNil(); + expect(config).notTo(beNil()); }); it(@"should return nil for -remotes", ^{ - expect(config.remotes).to.beNil(); + expect(config.remotes).to(beNil()); }); it(@"should support reading and writing", ^{ id value = [config stringForKey:testKey]; - expect(value).to.beNil(); + expect(value).to(beNil()); [config setString:testValue forKey:testKey]; value = [config stringForKey:testKey]; - expect(value).to.equal(testValue); + expect(value).to(equal(testValue)); }); it(@"should support deletion", ^{ [config setString:testValue forKey:testKey]; id value = [config stringForKey:testKey]; - expect(value).notTo.beNil(); + expect(value).notTo(beNil()); BOOL success = [config deleteValueForKey:testKey error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); value = [config stringForKey:testKey]; - expect(value).to.beNil(); + expect(value).to(beNil()); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTDiffDeltaSpec.m b/ObjectiveGitTests/GTDiffDeltaSpec.m new file mode 100644 index 000000000..81c1ae0e0 --- /dev/null +++ b/ObjectiveGitTests/GTDiffDeltaSpec.m @@ -0,0 +1,82 @@ +// +// GTDiffDeltaSpec.m +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 7/15/14. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTDiffDeltaSpec) + +__block GTRepository *repository; +__block GTDiffDelta *delta; + +beforeEach(^{ + repository = [self testAppFixtureRepository]; +}); + +describe(@"blob-to-blob diffing", ^{ + beforeEach(^{ + GTBlob *blob1 = [repository lookUpObjectBySHA:@"847cd4b33f4e33bc413468bab016303b50d26d95" error:NULL]; + expect(blob1).notTo(beNil()); + + GTBlob *blob2 = [repository lookUpObjectBySHA:@"6060bdeee91b02cb56d9826b4208e9b34122f3f1" error:NULL]; + expect(blob2).notTo(beNil()); + + delta = [GTDiffDelta diffDeltaFromBlob:blob1 forPath:@"README1.txt" toBlob:blob2 forPath:@"README1.txt" options:nil error:NULL]; + expect(delta).notTo(beNil()); + }); + + it(@"should generate a patch", ^{ + GTDiffPatch *patch = [delta generatePatch:NULL]; + expect(patch).notTo(beNil()); + expect(@(patch.hunkCount)).to(equal(@1)); + expect(@(patch.addedLinesCount)).to(equal(@1)); + expect(@(patch.deletedLinesCount)).to(equal(@1)); + }); +}); + +describe(@"blob-to-data diffing", ^{ + beforeEach(^{ + GTBlob *blob = [repository lookUpObjectBySHA:@"847cd4b33f4e33bc413468bab016303b50d26d95" error:NULL]; + expect(blob).notTo(beNil()); + + NSData *data = [@"hello, world" dataUsingEncoding:NSUTF8StringEncoding]; + + delta = [GTDiffDelta diffDeltaFromBlob:blob forPath:@"README" toData:data forPath:@"README" options:nil error:NULL]; + expect(delta).notTo(beNil()); + }); + + it(@"should generate a patch", ^{ + GTDiffPatch *patch = [delta generatePatch:NULL]; + expect(patch).notTo(beNil()); + expect(@(patch.hunkCount)).to(equal(@1)); + expect(@(patch.addedLinesCount)).to(equal(@1)); + expect(@(patch.deletedLinesCount)).to(equal(@26)); + }); +}); + +describe(@"data-to-data diffing", ^{ + beforeEach(^{ + NSData *data1 = [@"hello world!\nwhat's up" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *data2 = [@"hello, world" dataUsingEncoding:NSUTF8StringEncoding]; + delta = [GTDiffDelta diffDeltaFromData:data1 forPath:@"README" toData:data2 forPath:@"README" options:nil error:NULL]; + expect(delta).notTo(beNil()); + }); + + it(@"should generate a patch", ^{ + GTDiffPatch *patch = [delta generatePatch:NULL]; + expect(patch).notTo(beNil()); + expect(@(patch.hunkCount)).to(equal(@1)); + expect(@(patch.addedLinesCount)).to(equal(@1)); + expect(@(patch.deletedLinesCount)).to(equal(@2)); + }); +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTDiffSpec.m b/ObjectiveGitTests/GTDiffSpec.m index 304c7155d..0ad3eea82 100644 --- a/ObjectiveGitTests/GTDiffSpec.m +++ b/ObjectiveGitTests/GTDiffSpec.m @@ -6,56 +6,62 @@ // Copyright (c) 2012 GitHub, Inc. All rights reserved. // -SpecBegin(GTDiff) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTDiffSpec) __block GTRepository *repository = nil; describe(@"GTDiff initialisation", ^{ __block GTCommit *firstCommit = nil; __block GTCommit *secondCommit = nil; - + beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).toNot.beNil(); - + expect(repository).notTo(beNil()); + firstCommit = (GTCommit *)[repository lookUpObjectBySHA:@"8e0e65988d3007867a9f59ca8639ba975ef97e69" objectType:GTObjectTypeCommit error:NULL]; - expect(firstCommit).toNot.beNil(); - + expect(firstCommit).notTo(beNil()); + secondCommit = (GTCommit *)[repository lookUpObjectBySHA:@"a5840674db1a58cac0b2e7d046b627837a16f217" objectType:GTObjectTypeCommit error:NULL]; - expect(secondCommit).toNot.beNil(); + expect(secondCommit).notTo(beNil()); }); - + it(@"should be able to initialise a diff from 2 trees", ^{ - expect([GTDiff diffOldTree:firstCommit.tree withNewTree:secondCommit.tree inRepository:repository options:nil error:NULL]).toNot.beNil(); + expect([GTDiff diffOldTree:firstCommit.tree withNewTree:secondCommit.tree inRepository:repository options:nil error:NULL]).notTo(beNil()); }); - + it(@"should be able to initialise a diff against an empty tree", ^{ - expect([GTDiff diffOldTree:nil withNewTree:firstCommit.tree inRepository:repository options:nil error:NULL]).toNot.beNil(); - expect([GTDiff diffOldTree:firstCommit.tree withNewTree:nil inRepository:repository options:nil error:NULL]).toNot.beNil(); + expect([GTDiff diffOldTree:nil withNewTree:firstCommit.tree inRepository:repository options:nil error:NULL]).notTo(beNil()); + expect([GTDiff diffOldTree:firstCommit.tree withNewTree:nil inRepository:repository options:nil error:NULL]).notTo(beNil()); }); - + it(@"should be able to initialise a diff against the index with a tree", ^{ - expect([GTDiff diffIndexFromTree:secondCommit.tree inRepository:repository options:nil error:NULL]).toNot.beNil(); + expect([GTDiff diffIndexFromTree:secondCommit.tree inRepository:repository options:nil error:NULL]).notTo(beNil()); }); - + it(@"should be able to initialise a diff against the index without a tree", ^{ - expect([GTDiff diffIndexFromTree:nil inRepository:repository options:nil error:NULL]).toNot.beNil(); + expect([GTDiff diffIndexFromTree:nil inRepository:repository options:nil error:NULL]).notTo(beNil()); }); - + it(@"should be able to initialise a diff against a working directory and a tree", ^{ - expect([GTDiff diffWorkingDirectoryFromTree:firstCommit.tree inRepository:repository options:nil error:NULL]).toNot.beNil(); + expect([GTDiff diffWorkingDirectoryFromTree:firstCommit.tree inRepository:repository options:nil error:NULL]).notTo(beNil()); }); - + it(@"should be able to initialise a diff against a working directory and an empty tree", ^{ - expect([GTDiff diffWorkingDirectoryFromTree:nil inRepository:repository options:nil error:NULL]).toNot.beNil(); + expect([GTDiff diffWorkingDirectoryFromTree:nil inRepository:repository options:nil error:NULL]).notTo(beNil()); }); - + it(@"should be able to initialse a diff against an index from a repo's working directory", ^{ - expect([GTDiff diffIndexToWorkingDirectoryInRepository:repository options:nil error:NULL]).toNot.beNil(); + expect([GTDiff diffIndexToWorkingDirectoryInRepository:repository options:nil error:NULL]).notTo(beNil()); }); it(@"should be able to initialize a diff between HEAD and the working directory", ^{ - expect([GTDiff diffWorkingDirectoryToHEADInRepository:repository options:nil error:NULL]).notTo.beNil(); + expect([GTDiff diffWorkingDirectoryToHEADInRepository:repository options:nil error:NULL]).notTo(beNil()); }); }); @@ -64,51 +70,53 @@ __block GTCommit *secondCommit = nil; __block GTDiff *diff = nil; __block void (^setupDiffFromCommitSHAsAndOptions)(NSString *, NSString *, NSDictionary *) = nil; - + beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).toNot.beNil(); - + expect(repository).notTo(beNil()); + setupDiffFromCommitSHAsAndOptions = [^(NSString *firstCommitSHA, NSString *secondCommitSHA, NSDictionary *options) { firstCommit = (GTCommit *)[repository lookUpObjectBySHA:firstCommitSHA objectType:GTObjectTypeCommit error:NULL]; - expect(firstCommit).toNot.beNil(); + expect(firstCommit).notTo(beNil()); secondCommit = (GTCommit *)[repository lookUpObjectBySHA:secondCommitSHA objectType:GTObjectTypeCommit error:NULL]; - expect(secondCommit).toNot.beNil(); - + expect(secondCommit).notTo(beNil()); + diff = [GTDiff diffOldTree:firstCommit.tree withNewTree:secondCommit.tree inRepository:repository options:options error:NULL]; - expect(diff).toNot.beNil(); + expect(diff).notTo(beNil()); } copy]; }); - + it(@"should be able to diff simple file changes", ^{ setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", nil); - expect(diff.deltaCount).to.equal(1); - expect([diff numberOfDeltasWithType:GTDiffFileDeltaModified]).to.equal(1); - - [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); + expect(@(diff.deltaCount)).to(equal(@1)); + expect(@([diff numberOfDeltasWithType:GTDeltaTypeModified])).to(equal(@1)); + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { NSError *error = nil; GTDiffPatch *patch = [delta generatePatch:&error]; - expect(patch).notTo.beNil(); - expect(error).to.beNil(); + expect(patch).notTo(beNil()); + expect(error).to(beNil()); - expect(delta.oldFile.path).to.equal(@"TestAppWindowController.h"); - expect(delta.oldFile.path).to.equal(delta.newFile.path); - expect(delta.flags & GTDiffFileFlagBinaryMask).to.equal(GTDiffFileFlagNotBinary); - expect(delta.type).to.equal(GTDiffFileDeltaModified); + expect(delta.oldFile.path).to(equal(@"TestAppWindowController.h")); + expect(delta.oldFile.path).to(equal(delta.newFile.path)); + expect(@(delta.flags & GTDiffFileFlagBinaryMask)).to(equal(@(GTDiffFileFlagNotBinary))); + expect(@(delta.type)).to(equal(@(GTDeltaTypeModified))); - expect(patch.delta).to.beIdenticalTo(delta); - expect(patch.hunkCount).to.equal(1); - expect(patch.addedLinesCount).to.equal(1); - expect(patch.deletedLinesCount).to.equal(1); - expect(patch.contextLinesCount).to.equal(6); + expect(patch.delta).to(beIdenticalTo(delta)); + expect(@(patch.hunkCount)).to(equal(@1)); + expect(@(patch.addedLinesCount)).to(equal(@1)); + expect(@(patch.deletedLinesCount)).to(equal(@1)); + expect(@(patch.contextLinesCount)).to(equal(@6)); [patch enumerateHunksUsingBlock:^(GTDiffHunk *hunk, BOOL *stop) { - expect(hunk.header).to.equal(@"@@ -4,7 +4,7 @@"); - expect(hunk.lineCount).to.equal(8); - + expect(hunk.header).to(equal(@"@@ -4,7 +4,7 @@")); + expect(@(hunk.lineCount)).to(equal(@8)); + expect(@(hunk.oldStart)).to(equal(@4)); + expect(@(hunk.oldLines)).to(equal(@7)); + expect(@(hunk.newStart)).to(equal(@4)); + expect(@(hunk.newLines)).to(equal(@7)); + NSArray *expectedLines = @[ @"//", @"// Created by Joe Ricioppo on 9/29/10.", @"// Copyright 2010 __MyCompanyName__. All rights reserved.", @@ -117,184 +125,176 @@ @"", @"#import ", @"#import " ]; - + NSUInteger subtractionLine = 3; NSUInteger additionLine = 4; __block NSUInteger lineIndex = 0; [hunk enumerateLinesInHunk:NULL usingBlock:^(GTDiffLine *line, BOOL *stop) { - expect(line.content).to.equal(expectedLines[lineIndex]); + expect(line.content).to(equal(expectedLines[lineIndex])); if (lineIndex == subtractionLine) { - expect((NSUInteger)line.origin).to.equal(GTDiffLineOriginDeletion); + expect(@(line.origin)).to(equal(@(GTDiffLineOriginDeletion))); } else if (lineIndex == additionLine) { - expect((NSUInteger)line.origin).to.equal(GTDiffLineOriginAddition); + expect(@(line.origin)).to(equal(@(GTDiffLineOriginAddition))); } else { - expect((NSUInteger)line.origin).to.equal(GTDiffLineOriginContext); + expect(@(line.origin)).to(equal(@(GTDiffLineOriginContext))); } - + lineIndex ++; }]; }]; - + // just in case we have failed an above test, don't add a whole bunch // more false failures by iterating again. *stop = YES; }]; }); - + it(@"should recognised added files", ^{ setupDiffFromCommitSHAsAndOptions(@"4d5a6cc7a4d810be71bd47331c947b22580a5997", @"38f1e536cfc2ee41e07d55b38baec00149b2b0d1", nil); - expect(diff.deltaCount).to.equal(1); + expect(@(diff.deltaCount)).to(equal(@1)); [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); - expect(delta.newFile.path).to.equal(@"REAME"); //loltypo - expect(delta.type).to.equal(GTDiffFileDeltaAdded); + expect(delta.newFile.path).to(equal(@"REAME")); //loltypo + expect(@(delta.type)).to(equal(@(GTDeltaTypeAdded))); *stop = YES; }]; }); - + it(@"should recognise deleted files", ^{ setupDiffFromCommitSHAsAndOptions(@"6317779b4731d9c837dcc6972b964bdf4211eeef", @"9f90c6e24629fae3ef51101bb6448342b44098ef", nil); - expect(diff.deltaCount).to.equal(1); + expect(@(diff.deltaCount)).to(equal(@1)); [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); - expect(delta.type).to.equal(GTDiffFileDeltaDeleted); + expect(@(delta.type)).to(equal(@(GTDeltaTypeDeleted))); *stop = YES; }]; }); - + it(@"should recognise binary files", ^{ setupDiffFromCommitSHAsAndOptions(@"2ba9cdca982ac35a8db29f51c635251374008229", @"524500582248889ef2243931aa7fc48aa21dd12f", nil); - expect(diff.deltaCount).to.equal(1); + expect(@(diff.deltaCount)).to(equal(@1)); [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { // Determine binary/not binary status. [delta generatePatch:NULL]; - expect(delta.diff).to.beIdenticalTo(diff); - expect(delta.flags & GTDiffFileFlagBinaryMask).to.equal(GTDiffFileFlagBinary); + expect(@(delta.flags & GTDiffFileFlagBinaryMask)).to(equal(@(GTDiffFileFlagBinary))); *stop = YES; }]; - + }); - + it(@"should recognise renames", ^{ setupDiffFromCommitSHAsAndOptions(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0", @"6b0c1c8b8816416089c534e474f4c692a76ac14f", nil); [diff findSimilarWithOptions:nil]; - expect(diff.deltaCount).to.equal(1); + expect(@(diff.deltaCount)).to(equal(@1)); [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); - expect(delta.type).to.equal(GTDiffFileDeltaRenamed); - expect(delta.oldFile.path).to.equal(@"README"); - expect(delta.newFile.path).to.equal(@"README_renamed"); + expect(@(delta.type)).to(equal(@(GTDeltaTypeRenamed))); + expect(delta.oldFile.path).to(equal(@"README")); + expect(delta.newFile.path).to(equal(@"README_renamed")); *stop = YES; }]; }); - + it(@"should correctly pass options to libgit2", ^{ NSDictionary *options = @{ GTDiffOptionsContextLinesKey: @(5) }; setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", options); - expect(diff.deltaCount).to.equal(1); + expect(@(diff.deltaCount)).to(equal(@1)); [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); - NSError *error = nil; GTDiffPatch *patch = [delta generatePatch:&error]; - expect(patch).notTo.beNil(); - expect(error).to.beNil(); + expect(patch).notTo(beNil()); + expect(error).to(beNil()); - expect(patch.hunkCount).to.equal(1); - expect(patch.delta).to.beIdenticalTo(delta); + expect(@(patch.hunkCount)).to(equal(@1)); + expect(patch.delta).to(beIdenticalTo(delta)); [patch enumerateHunksUsingBlock:^(GTDiffHunk *hunk, BOOL *stop) { __block NSUInteger contextCount = 0; [hunk enumerateLinesInHunk:NULL usingBlock:^(GTDiffLine *line, BOOL *stop) { if (line.origin == GTDiffLineOriginContext) contextCount ++; }]; - expect(contextCount).to.equal(10); + expect(@(contextCount)).to(equal(@10)); *stop = YES; }]; *stop = YES; }]; }); - + it(@"should correctly limit itself to a given pathspec", ^{ NSDictionary *options = @{ GTDiffOptionsPathSpecArrayKey: @[ @"ladflbahjgdf" ] }; setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", options); - expect(diff.deltaCount).to.equal(0); - + expect(@(diff.deltaCount)).to(equal(@0)); + options = @{ GTDiffOptionsPathSpecArrayKey: @[ @"TestAppWindowController.h" ] }; setupDiffFromCommitSHAsAndOptions(@"be0f001ff517a00b5b8e3c29ee6561e70f994e17", @"fe89ea0a8e70961b8a6344d9660c326d3f2eb0fe", options); - expect(diff.deltaCount).to.equal(1); + expect(@(diff.deltaCount)).to(equal(@1)); }); - + it(@"should correctly recognise binary and text files", ^{ setupDiffFromCommitSHAsAndOptions(@"6b0c1c8b8816416089c534e474f4c692a76ac14f", @"a4bca6b67a5483169963572ee3da563da33712f7", nil); - expect(diff.deltaCount).to.equal(3); - + expect(@(diff.deltaCount)).to(equal(@3)); + NSDictionary *expectedBinaryness = @{ @"README.md": @(NO), @"hero_slide1.png": @(YES), @"jquery-1.8.1.min.js": @(NO) }; [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); - // Determine binary/not binary status. [delta generatePatch:NULL]; if ([expectedBinaryness[delta.newFile.path] boolValue]) { - expect(delta.flags & GTDiffFileFlagBinaryMask).to.equal(GTDiffFileFlagBinary); + expect(@(delta.flags & GTDiffFileFlagBinaryMask)).to(equal(@(GTDiffFileFlagBinary))); } else { - expect(delta.flags & GTDiffFileFlagBinaryMask).to.equal(GTDiffFileFlagNotBinary); + expect(@(delta.flags & GTDiffFileFlagBinaryMask)).to(equal(@(GTDiffFileFlagNotBinary))); } }]; }); - + it(@"shouldn't choke on totally cray diffs", ^{ setupDiffFromCommitSHAsAndOptions(@"6b0c1c8b8816416089c534e474f4c692a76ac14f", @"a4bca6b67a5483169963572ee3da563da33712f7", nil); - - [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); + [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { if (![delta.newFile.path isEqualToString:@"jquery-1.8.1.min.js"]) return; NSError *error = nil; GTDiffPatch *patch = [delta generatePatch:&error]; - expect(patch).notTo.beNil(); - expect(error).to.beNil(); - - expect(patch.hunkCount).to.equal(1); - expect(patch.delta).to.beIdenticalTo(delta); + expect(patch).notTo(beNil()); + expect(error).to(beNil()); + + expect(@(patch.hunkCount)).to(equal(@1)); + expect(patch.delta).to(beIdenticalTo(delta)); [patch enumerateHunksUsingBlock:^(GTDiffHunk *hunk, BOOL *stop) { - expect(hunk.lineCount).to.equal(3); + expect(@(hunk.lineCount)).to(equal(@3)); *stop = YES; }]; - + *stop = YES; }]; }); - + it(@"should correctly find untracked files if asked", ^{ diff = [GTDiff diffIndexToWorkingDirectoryInRepository:repository options:@{ GTDiffOptionsFlagsKey: @(GTDiffOptionsFlagsIncludeUntracked) } error:NULL]; __block BOOL foundImage = NO; [diff enumerateDeltasUsingBlock:^(GTDiffDelta *delta, BOOL *stop) { - expect(delta.diff).to.beIdenticalTo(diff); - if (![delta.newFile.path isEqualToString:@"UntrackedImage.png"]) return; - foundImage = YES; + foundImage = YES; *stop = YES; }]; - - expect(foundImage).to.beTruthy(); + + expect(@(foundImage)).to(beTruthy()); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTEnumeratorSpec.m b/ObjectiveGitTests/GTEnumeratorSpec.m index 3c4671b59..70f4e4784 100644 --- a/ObjectiveGitTests/GTEnumeratorSpec.m +++ b/ObjectiveGitTests/GTEnumeratorSpec.m @@ -6,37 +6,43 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTEnumerator.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTEnumerator) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTEnumeratorSpec) __block GTRepository *repo; __block GTEnumerator *enumerator; beforeEach(^{ repo = self.bareFixtureRepository; - expect(repo).notTo.beNil(); + expect(repo).notTo(beNil()); enumerator = [[GTEnumerator alloc] initWithRepository:repo error:NULL]; - expect(enumerator).notTo.beNil(); + expect(enumerator).notTo(beNil()); }); it(@"should walk from repository HEAD", ^{ NSError *error = nil; GTReference *HEADRef = [repo headReferenceWithError:NULL]; - expect(HEADRef).notTo.beNil(); - - [enumerator pushSHA:HEADRef.targetSHA error:NULL]; + expect(HEADRef).notTo(beNil()); + + BOOL success = [enumerator pushSHA:HEADRef.targetOID.SHA error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + NSUInteger count = [enumerator allObjects].count; - expect(count).to.equal(3); - expect(error).to.beNil(); + expect(@(count)).to(equal(@3)); }); describe(@"with a rev list", ^{ __block NSMutableArray *expectedSHAs; __block void (^verifyEnumerator)(void); - + beforeEach(^{ expectedSHAs = [@[ @"9fd738e8f7967c078dceed8190330fc8648ee56a", @@ -47,21 +53,21 @@ verifyEnumerator = ^{ __block NSError *error = nil; - expect([enumerator pushSHA:expectedSHAs[0] error:&error]).to.beTruthy(); - expect(error).to.beNil(); + expect(@([enumerator pushSHA:expectedSHAs[0] error:&error])).to(beTruthy()); + expect(error).to(beNil()); NSMutableArray *SHAs = [NSMutableArray array]; for (GTCommit *commit in enumerator) { - expect(commit).to.beKindOf(GTCommit.class); + expect(commit).to(beAnInstanceOf(GTCommit.class)); [SHAs addObject:commit.SHA]; } - expect(SHAs).to.equal(expectedSHAs); + expect(SHAs).to(equal(expectedSHAs)); __block BOOL success; - expect([enumerator nextObjectWithSuccess:&success error:&error]).to.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect([enumerator nextObjectWithSuccess:&success error:&error]).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }; }); @@ -77,20 +83,20 @@ it(@"should hide a SHA", ^{ __block NSError *error = nil; - expect([enumerator hideSHA:expectedSHAs[2] error:&error]).to.beTruthy(); - expect(error).to.beNil(); + expect(@([enumerator hideSHA:expectedSHAs[2] error:&error])).to(beTruthy()); + expect(error).to(beNil()); [expectedSHAs removeObjectsInRange:NSMakeRange(2, expectedSHAs.count - 2)]; verifyEnumerator(); }); it(@"should reset with options", ^{ - expect(enumerator.options).to.equal(GTEnumeratorOptionsNone); + expect(@(enumerator.options)).to(equal(@(GTEnumeratorOptionsNone))); verifyEnumerator(); [enumerator resetWithOptions:GTEnumeratorOptionsTimeSort]; - expect(enumerator.options).to.equal(GTEnumeratorOptionsTimeSort); + expect(@(enumerator.options)).to(equal(@(GTEnumeratorOptionsTimeSort))); verifyEnumerator(); }); }); @@ -100,7 +106,7 @@ __block NSMutableArray *expectedSHAs; __block void (^verifyEnumerator)(void); - + beforeEach(^{ expectedSHAs = [@[ @"36060c58702ed4c2a40832c51758d5344201d89a", @@ -114,35 +120,39 @@ [SHAs addObject:commit.SHA]; } - expect(SHAs).to.equal(expectedSHAs); + expect(SHAs).to(equal(expectedSHAs)); __block NSError *error = nil; __block BOOL success; - expect([enumerator nextObjectWithSuccess:&success error:&error]).to.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect([enumerator nextObjectWithSuccess:&success error:&error]).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }; }); it(@"should push a glob", ^{ __block NSError *error = nil; - expect([enumerator pushGlob:branchGlob error:&error]).to.beTruthy(); - expect(error).to.beNil(); - + expect(@([enumerator pushGlob:branchGlob error:&error])).to(beTruthy()); + expect(error).to(beNil()); + verifyEnumerator(); }); it(@"should hide a glob", ^{ __block NSError *error = nil; - expect([enumerator pushSHA:expectedSHAs[0] error:&error]).to.beTruthy(); - expect(error).to.beNil(); + expect(@([enumerator pushSHA:expectedSHAs[0] error:&error])).to(beTruthy()); + expect(error).to(beNil()); + + expect(@([enumerator hideGlob:branchGlob error:&error])).to(beTruthy()); + expect(error).to(beNil()); - expect([enumerator hideGlob:branchGlob error:&error]).to.beTruthy(); - expect(error).to.beNil(); - [expectedSHAs removeAllObjects]; verifyEnumerator(); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTFilterListSpec.m b/ObjectiveGitTests/GTFilterListSpec.m index 94c29d134..97313a562 100644 --- a/ObjectiveGitTests/GTFilterListSpec.m +++ b/ObjectiveGitTests/GTFilterListSpec.m @@ -6,10 +6,13 @@ // Copyright (c) 2014 GitHub, Inc. All rights reserved. // -#import "GTFilter.h" -#import "GTFilterList.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTFilterList) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTFilterListSpec) __block GTRepository *repository; @@ -24,7 +27,7 @@ NSString *attributes = @"READ* rf=true\n*.txt tf=true\n"; BOOL success = [attributes writeToURL:[repository.fileURL URLByAppendingPathComponent:@".gitattributes"] atomically:YES encoding:NSUTF8StringEncoding error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); readFilter = [[GTFilter alloc] initWithName:@"read-filter" attributes:@"rf=true" applyBlock:^(void **payload, NSData *from, GTFilterSource *source, BOOL *applied) { NSMutableData *buffer = [from mutableCopy]; @@ -33,8 +36,8 @@ return buffer; }]; - expect(readFilter).notTo.beNil(); - expect([readFilter registerWithPriority:1 error:NULL]).to.beTruthy(); + expect(readFilter).notTo(beNil()); + expect(@([readFilter registerWithPriority:1 error:NULL])).to(beTruthy()); textFilter = [[GTFilter alloc] initWithName:@"text-filter" attributes:@"tf=true" applyBlock:^(void **payload, NSData *from, GTFilterSource *source, BOOL *applied) { NSMutableData *buffer = [from mutableCopy]; @@ -43,13 +46,13 @@ return buffer; }]; - expect(textFilter).notTo.beNil(); - expect([textFilter registerWithPriority:0 error:NULL]).to.beTruthy(); + expect(textFilter).notTo(beNil()); + expect(@([textFilter registerWithPriority:0 error:NULL])).to(beTruthy()); }); afterEach(^{ - expect([readFilter unregister:NULL]).to.beTruthy(); - expect([textFilter unregister:NULL]).to.beTruthy(); + expect(@([readFilter unregister:NULL])).to(beTruthy()); + expect(@([textFilter unregister:NULL])).to(beTruthy()); }); describe(@"loading a filter list", ^{ @@ -63,52 +66,52 @@ it(@"should return nil on a path without any filters", ^{ GTFilterList *list = [repository filterListWithPath:@"TestAppDelegate.h" blob:nil mode:GTFilterSourceModeSmudge options:GTFilterListOptionsDefault success:&success error:&error]; - expect(list).to.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(list).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); it(@"should return non-nil on a path with a single filter", ^{ GTFilterList *list = [repository filterListWithPath:@"README.md" blob:nil mode:GTFilterSourceModeSmudge options:GTFilterListOptionsDefault success:&success error:&error]; - expect(list).notTo.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(list).notTo(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); it(@"should return non-nil on a path with multiple filters", ^{ GTFilterList *list = [repository filterListWithPath:@"README1.txt" blob:nil mode:GTFilterSourceModeSmudge options:GTFilterListOptionsDefault success:&success error:&error]; - expect(list).notTo.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(list).notTo(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); it(@"should return non-nil on a nonexistent path with a blob", ^{ NSData *data = [@"haters gonna haaaate" dataUsingEncoding:NSUTF8StringEncoding]; GTBlob *blob = [[GTBlob alloc] initWithData:data inRepository:repository error:NULL]; - expect(blob).notTo.beNil(); + expect(blob).notTo(beNil()); GTFilterList *list = [repository filterListWithPath:@"haters-gonna-hate.txt" blob:blob mode:GTFilterSourceModeClean options:GTFilterListOptionsDefault success:&success error:&error]; - expect(list).notTo.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(list).notTo(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); }); it(@"should apply a single filter", ^{ GTFilterList *list = [repository filterListWithPath:@"README.md" blob:nil mode:GTFilterSourceModeSmudge options:GTFilterListOptionsDefault success:NULL error:NULL]; - expect(list).notTo.beNil(); + expect(list).notTo(beNil()); NSString *inputString = @"foobar"; NSError *error = nil; NSData *result = [list applyToData:[inputString dataUsingEncoding:NSUTF8StringEncoding] error:&error]; - expect(result).notTo.beNil(); - expect(error).to.beNil(); + expect(result).notTo(beNil()); + expect(error).to(beNil()); NSString *resultString = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; - expect(resultString).to.contain(inputString); - expect(resultString).to.contain(readFilterContent); - expect(resultString).notTo.contain(textFilterContent); + expect(resultString).to(beginWith(inputString)); + expect(resultString).to(endWith(readFilterContent)); + expect(resultString).notTo(contain(textFilterContent)); }); describe(@"applying a list of multiple filters", ^{ @@ -117,7 +120,7 @@ beforeEach(^{ // This file should have `readFilter` applied first, then `textFilter`. list = [repository filterListWithPath:@"README1.txt" blob:nil mode:GTFilterSourceModeSmudge options:GTFilterListOptionsDefault success:NULL error:NULL]; - expect(list).notTo.beNil(); + expect(list).notTo(beNil()); }); afterEach(^{ @@ -130,13 +133,13 @@ NSError *error = nil; NSData *result = [list applyToData:[inputString dataUsingEncoding:NSUTF8StringEncoding] error:&error]; - expect(result).notTo.beNil(); - expect(error).to.beNil(); + expect(result).notTo(beNil()); + expect(error).to(beNil()); NSString *resultString = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; - expect(resultString).to.contain(inputString); - expect(resultString).to.contain(readFilterContent); - expect(resultString).to.contain(textFilterContent); + expect(resultString).to(beginWith(inputString)); + expect(resultString).to(endWith(readFilterContent)); + expect(@([resultString rangeOfString:textFilterContent].location)).notTo(equal(@(NSNotFound))); }); it(@"should apply to a file", ^{ @@ -144,39 +147,43 @@ GTRepository *inputRepo = self.conflictedFixtureRepository; NSString *content = [NSString stringWithContentsOfURL:[inputRepo.fileURL URLByAppendingPathComponent:inputFilename] encoding:NSUTF8StringEncoding error:NULL]; - expect(content).notTo.contain(readFilterContent); - expect(content).notTo.contain(textFilterContent); + expect(content).notTo(contain(readFilterContent)); + expect(content).notTo(contain(textFilterContent)); NSError *error = nil; NSData *result = [list applyToPath:inputFilename inRepository:inputRepo error:&error]; - expect(result).notTo.beNil(); - expect(error).to.beNil(); + expect(result).notTo(beNil()); + expect(error).to(beNil()); NSString *resultString = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; - expect(resultString).to.contain(content); - expect(resultString).to.contain(readFilterContent); - expect(resultString).to.contain(textFilterContent); + expect(resultString).to(beginWith(content)); + expect(resultString).to(endWith(readFilterContent)); + expect(@([resultString rangeOfString:textFilterContent].location)).notTo(equal(@(NSNotFound))); }); it(@"should apply to a blob", ^{ // This is `REAME_` from `HEAD`. GTBlob *blob = [repository lookUpObjectBySHA:@"8b4a21733703ca50b96186691615e8d2f6314e79" objectType:GTObjectTypeBlob error:NULL]; - expect(blob).notTo.beNil(); + expect(blob).notTo(beNil()); - expect(blob.content).notTo.beNil(); - expect(blob.content).notTo.contain(readFilterContent); - expect(blob.content).notTo.contain(textFilterContent); + expect(blob.content).notTo(beNil()); + expect(blob.content).notTo(contain(readFilterContent)); + expect(blob.content).notTo(contain(textFilterContent)); NSError *error = nil; NSData *result = [list applyToBlob:blob error:&error]; - expect(result).notTo.beNil(); - expect(error).to.beNil(); + expect(result).notTo(beNil()); + expect(error).to(beNil()); NSString *resultString = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; - expect(resultString).to.contain(blob.content); - expect(resultString).to.contain(readFilterContent); - expect(resultString).to.contain(textFilterContent); + expect(resultString).to(beginWith(blob.content)); + expect(resultString).to(endWith(readFilterContent)); + expect(@([resultString rangeOfString:textFilterContent].location)).notTo(equal(@(NSNotFound))); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTFilterSpec.m b/ObjectiveGitTests/GTFilterSpec.m index 94ad5844d..7ab7a5597 100644 --- a/ObjectiveGitTests/GTFilterSpec.m +++ b/ObjectiveGitTests/GTFilterSpec.m @@ -6,9 +6,13 @@ // Copyright (c) 2014 GitHub, Inc. All rights reserved. // -#import "GTFilter.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTFilter) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTFilterSpec) static NSString * const testFile = @"stuff.txt"; static NSString * const filterName = @"special-filter"; @@ -25,14 +29,14 @@ beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); NSString *attributes = @"*.txt special\n"; BOOL success = [attributes writeToURL:[repository.fileURL URLByAppendingPathComponent:@".gitattributes"] atomically:YES encoding:NSUTF8StringEncoding error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); success = [@"some stuff" writeToURL:[repository.fileURL URLByAppendingPathComponent:testFile] atomically:YES encoding:NSUTF8StringEncoding error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); setUpFilterWithApplyBlock = ^(GTFilterApplyBlock applyBlock) { applyBlock = applyBlock ?: ^ NSData * (void **payload, NSData *from, GTFilterSource *source, BOOL *applied) { @@ -42,30 +46,30 @@ filter = [[GTFilter alloc] initWithName:filterName attributes:filterAttributes applyBlock:applyBlock]; BOOL success = [filter registerWithPriority:0 error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); }; addTestFileToIndex = ^{ GTIndex *index = [repository indexWithError:NULL]; - expect(index).notTo.beNil(); + expect(index).notTo(beNil()); BOOL success = [index addFile:@"stuff.txt" error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); success = [index write:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); }; }); afterEach(^{ BOOL success = [filter unregister:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); }); it(@"should be able to look up a registered filter by name", ^{ setUpFilterWithApplyBlock(nil); GTFilter *filter = [GTFilter filterForName:filterName]; - expect(filter).notTo.beNil(); + expect(filter).notTo(beNil()); }); it(@"should call all the blocks", ^{ @@ -93,10 +97,10 @@ addTestFileToIndex(); - expect(initializeCalled).to.beTruthy(); - expect(checkCalled).to.beTruthy(); - expect(applyCalled).to.beTruthy(); - expect(cleanupCalled).to.beTruthy(); + expect(@(initializeCalled)).to(beTruthy()); + expect(@(checkCalled)).to(beTruthy()); + expect(@(applyCalled)).to(beTruthy()); + expect(@(cleanupCalled)).to(beTruthy()); }); it(@"shouldn't call the apply block if the check block returns NO", ^{ @@ -112,7 +116,7 @@ addTestFileToIndex(); - expect(applyCalled).to.beFalsy(); + expect(@(applyCalled)).to(beFalsy()); }); describe(@"application", ^{ @@ -128,23 +132,23 @@ GTTree *tree = [index writeTree:NULL]; GTTreeEntry *entry = [tree entryWithName:testFile]; GTOdbObject *ODBObject = [[entry GTObject:NULL] odbObjectWithError:NULL]; - expect(ODBObject.data).to.equal(replacementData); + expect(ODBObject.data).to(equal(replacementData)); }); it(@"should write the data returned by the apply block when smudged", ^{ addTestFileToIndex(); GTIndex *index = [repository indexWithError:NULL]; GTTree *tree = [index writeTree:NULL]; - expect(tree).notTo.beNil(); + expect(tree).notTo(beNil()); GTReference *HEADRef = [repository headReferenceWithError:NULL]; - expect(HEADRef).notTo.beNil(); + expect(HEADRef).notTo(beNil()); GTCommit *HEADCommit = HEADRef.resolvedTarget; - expect(HEADCommit).notTo.beNil(); + expect(HEADCommit).notTo(beNil()); GTCommit *newCommit = [repository createCommitWithTree:tree message:@"" parents:@[ HEADCommit ] updatingReferenceNamed:HEADRef.name error:NULL]; - expect(newCommit).notTo.beNil(); + expect(newCommit).notTo(beNil()); NSData *replacementData = [@"you're my favorite customer" dataUsingEncoding:NSUTF8StringEncoding]; setUpFilterWithApplyBlock(^(void **payload, NSData *from, GTFilterSource *source, BOOL *applied) { @@ -153,12 +157,12 @@ NSURL *testFileURL = [repository.fileURL URLByAppendingPathComponent:testFile]; BOOL success = [NSFileManager.defaultManager removeItemAtURL:testFileURL error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); - success = [repository checkoutCommit:newCommit strategy:GTCheckoutStrategyForce error:NULL progressBlock:NULL]; - expect(success).to.beTruthy(); + success = [repository checkoutCommit:newCommit options:[GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategyForce] error:NULL]; + expect(@(success)).to(beTruthy()); - expect([NSData dataWithContentsOfURL:testFileURL]).to.equal(replacementData); + expect([NSData dataWithContentsOfURL:testFileURL]).to(equal(replacementData)); }); }); @@ -173,10 +177,14 @@ addTestFileToIndex(); - expect(filterSource).notTo.beNil(); - expect(filterSource.path).to.equal(testFile); - expect(filterSource.mode).to.equal(GTFilterSourceModeClean); - expect(filterSource.repositoryURL).to.equal(repository.fileURL); + expect(filterSource).notTo(beNil()); + expect(filterSource.path).to(equal(testFile)); + expect(@(filterSource.mode)).to(equal(@(GTFilterSourceModeClean))); + expect(filterSource.repositoryURL).to(equal(repository.fileURL)); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTIndexSpec.m b/ObjectiveGitTests/GTIndexSpec.m index 890733d3e..4e894153a 100644 --- a/ObjectiveGitTests/GTIndexSpec.m +++ b/ObjectiveGitTests/GTIndexSpec.m @@ -6,94 +6,94 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTIndex.h" -#import "GTConfiguration.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTIndex) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTIndexSpec) __block GTRepository *repository; __block GTIndex *index; beforeEach(^{ - repository = self.bareFixtureRepository; + repository = self.testAppFixtureRepository; - NSURL *indexURL = [repository.gitDirectoryURL URLByAppendingPathComponent:@"index"]; - index = [GTIndex indexWithFileURL:indexURL repository:self.bareFixtureRepository error:NULL]; - expect(index).notTo.beNil(); + index = [repository indexWithError:NULL]; + expect(index).notTo(beNil()); BOOL success = [index refresh:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); }); it(@"should count the entries", ^{ - expect(index.entryCount).to.equal(2); + expect(@(index.entryCount)).to(equal(@24)); }); it(@"should clear all entries", ^{ - [index clear]; - expect(index.entryCount).to.equal(0); + [index clear:NULL]; + expect(@(index.entryCount)).to(equal(@0)); }); it(@"should read entry properties", ^{ GTIndexEntry *entry = [index entryAtIndex:0]; - expect(entry).notTo.beNil(); - expect(entry.path).to.equal(@"README"); - expect(entry.staged).to.beFalsy(); + expect(entry).notTo(beNil()); + expect(entry.path).to(equal(@".gitignore")); + expect(@(entry.staged)).to(beFalsy()); }); it(@"should write to the repository and return a tree", ^{ - GTRepository *repository = self.bareFixtureRepository; - GTIndex *index = [repository indexWithError:NULL]; GTTree *tree = [index writeTree:NULL]; - expect(tree).notTo.beNil(); - expect(tree.entryCount).to.equal(2); - expect(tree.repository).to.equal(repository); + expect(tree).notTo(beNil()); + expect(@(tree.entryCount)).to(equal(@23)); + expect(tree.repository).to(equal(repository)); }); it(@"should write to a specific repository and return a tree", ^{ GTRepository *repository = self.bareFixtureRepository; - NSArray *branches = [repository allBranchesWithError:NULL]; - GTCommit *masterCommit = [branches[0] targetCommitAndReturnError:NULL]; - GTCommit *packedCommit = [branches[1] targetCommitAndReturnError:NULL]; - - expect(masterCommit).notTo.beNil(); - expect(packedCommit).notTo.beNil(); - + NSArray *branches = [repository branches:NULL]; + GTCommit *masterCommit = [branches[0] targetCommitWithError:NULL]; + GTCommit *packedCommit = [branches[1] targetCommitWithError:NULL]; + + expect(masterCommit).notTo(beNil()); + expect(packedCommit).notTo(beNil()); + GTIndex *index = [masterCommit.tree merge:packedCommit.tree ancestor:NULL error:NULL]; GTTree *mergedTree = [index writeTreeToRepository:repository error:NULL]; - - expect(index).notTo.beNil(); - expect(mergedTree).notTo.beNil(); - expect(mergedTree.entryCount).to.equal(5); - expect(mergedTree.repository).to.equal(repository); + + expect(index).notTo(beNil()); + expect(mergedTree).notTo(beNil()); + expect(@(mergedTree.entryCount)).to(equal(@5)); + expect(mergedTree.repository).to(equal(repository)); }); it(@"should create an index in memory", ^{ GTIndex *memoryIndex = [GTIndex inMemoryIndexWithRepository:repository error:NULL]; - expect(memoryIndex).notTo.beNil(); - expect(memoryIndex.fileURL).to.beNil(); + expect(memoryIndex).notTo(beNil()); + expect(memoryIndex.fileURL).to(beNil()); }); it(@"should add the contents of a tree", ^{ GTCommit *headCommit = [repository lookUpObjectByRevParse:@"HEAD" error:NULL]; - expect(headCommit).notTo.beNil(); + expect(headCommit).notTo(beNil()); GTTree *headTree = headCommit.tree; - expect(headTree.entryCount).to.beGreaterThan(0); + expect(@(headTree.entryCount)).to(beGreaterThan(@0)); GTIndex *memoryIndex = [GTIndex inMemoryIndexWithRepository:index.repository error:NULL]; - expect(memoryIndex).notTo.beNil(); - expect(memoryIndex.entryCount).to.equal(0); + expect(memoryIndex).notTo(beNil()); + expect(@(memoryIndex.entryCount)).to(equal(@0)); BOOL success = [memoryIndex addContentsOfTree:headTree error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); [headTree enumerateEntriesWithOptions:GTTreeEnumerationOptionPre error:NULL block:^(GTTreeEntry *treeEntry, NSString *root, BOOL *stop) { if (treeEntry.type == GTObjectTypeBlob) { NSString *path = [root stringByAppendingString:treeEntry.name]; - GTIndexEntry *indexEntry = [memoryIndex entryWithName:path]; - expect(indexEntry).notTo.beNil(); + GTIndexEntry *indexEntry = [memoryIndex entryWithPath:path]; + expect(indexEntry).notTo(beNil()); } return YES; @@ -102,39 +102,39 @@ describe(@"conflict enumeration", ^{ it(@"should correctly find no conflicts", ^{ - expect(index.hasConflicts).to.beFalsy(); + expect(@(index.hasConflicts)).to(beFalsy()); }); - + it(@"should immediately return YES when enumerating no conflicts", ^{ __block BOOL blockRan = NO; BOOL enumerationResult = [index enumerateConflictedFilesWithError:NULL usingBlock:^(GTIndexEntry *ancestor, GTIndexEntry *ours, GTIndexEntry *theirs, BOOL *stop) { blockRan = YES; }]; - expect(enumerationResult).to.beTruthy(); - expect(blockRan).to.beFalsy(); + expect(@(enumerationResult)).to(beTruthy()); + expect(@(blockRan)).to(beFalsy()); }); - + it(@"should correctly report conflicts", ^{ index = [self.conflictedFixtureRepository indexWithError:NULL]; - expect(index).notTo.beNil(); - expect(index.hasConflicts).to.beTruthy(); + expect(index).notTo(beNil()); + expect(@(index.hasConflicts)).to(beTruthy()); }); - + it(@"should enumerate conflicts successfully", ^{ index = [self.conflictedFixtureRepository indexWithError:NULL]; - expect(index).notTo.beNil(); - + expect(index).notTo(beNil()); + NSError *err = NULL; __block NSUInteger count = 0; NSArray *expectedPaths = @[ @"TestAppDelegate.h", @"main.m" ]; BOOL enumerationResult = [index enumerateConflictedFilesWithError:&err usingBlock:^(GTIndexEntry *ancestor, GTIndexEntry *ours, GTIndexEntry *theirs, BOOL *stop) { - expect(ours.path).to.equal(expectedPaths[count]); + expect(ours.path).to(equal(expectedPaths[count])); count ++; }]; - - expect(enumerationResult).to.beTruthy(); - expect(err).to.beNil(); - expect(count).to.equal(2); + + expect(@(enumerationResult)).to(beTruthy()); + expect(err).to(beNil()); + expect(@(count)).to(equal(@2)); }); }); @@ -145,21 +145,21 @@ NSString *filePath = [self.testAppFixtureRepository.fileURL.path stringByAppendingPathComponent:fileName]; [@"The wild west..." writeToFile:filePath atomically:NO encoding:NSUTF8StringEncoding error:NULL]; - expect(index).toNot.beNil(); - expect([index.repository statusForFile:fileName success:NULL error:NULL]).to.equal(GTFileStatusModifiedInWorktree); + expect(index).notTo(beNil()); + expect(@([index.repository statusForFile:fileName success:NULL error:NULL])).to(equal(@(GTFileStatusModifiedInWorktree))); }); - + it(@"should update the Index", ^{ BOOL success = [index updatePathspecs:@[ fileName ] error:NULL passingTest:^(NSString *matchedPathspec, NSString *path, BOOL *stop) { - expect(matchedPathspec).to.equal(fileName); - expect(path).to.equal(fileName); + expect(matchedPathspec).to(equal(fileName)); + expect(path).to(equal(fileName)); return YES; }]; - - expect(success).to.beTruthy(); - expect([index.repository statusForFile:fileName success:NULL error:NULL]).to.equal(GTFileStatusModifiedInIndex); + + expect(@(success)).to(beTruthy()); + expect(@([index.repository statusForFile:fileName success:NULL error:NULL])).to(equal(@(GTFileStatusModifiedInIndex))); }); - + it(@"should skip a specific file", ^{ BOOL success = [index updatePathspecs:NULL error:NULL passingTest:^(NSString *matchedPathspec, NSString *path, BOOL *stop) { if ([path.lastPathComponent isEqualToString:fileName]) { @@ -168,11 +168,11 @@ return YES; } }]; - - expect(success).to.beTruthy(); - expect([index.repository statusForFile:fileName success:NULL error:NULL]).to.equal(GTFileStatusModifiedInWorktree); + + expect(@(success)).to(beTruthy()); + expect(@([index.repository statusForFile:fileName success:NULL error:NULL])).to(equal(@(GTFileStatusModifiedInWorktree))); }); - + it(@"should stop be able to stop early", ^{ NSString *otherFileName = @"TestAppDelegate.h"; [@"WELP" writeToFile:[self.testAppFixtureRepository.fileURL.path stringByAppendingPathComponent:otherFileName] atomically:NO encoding:NSUTF8StringEncoding error:NULL]; @@ -183,10 +183,10 @@ } return YES; }]; - - expect(success).to.beTruthy(); - expect([index.repository statusForFile:fileName success:NULL error:NULL]).to.equal(GTFileStatusModifiedInIndex); - expect([index.repository statusForFile:otherFileName success:NULL error:NULL]).equal(GTFileStatusModifiedInWorktree); + + expect(@(success)).to(beTruthy()); + expect(@([index.repository statusForFile:fileName success:NULL error:NULL])).to(equal(@(GTFileStatusModifiedInIndex))); + expect(@([index.repository statusForFile:otherFileName success:NULL error:NULL])).to(equal(@(GTFileStatusModifiedInWorktree))); }); }); @@ -202,21 +202,21 @@ NSDictionary *renamedOptions = @{ GTRepositoryStatusOptionsFlagsKey: @(GTRepositoryStatusFlagsIncludeIgnored | GTRepositoryStatusFlagsIncludeUntracked | GTRepositoryStatusFlagsRecurseUntrackedDirectories | GTRepositoryStatusFlagsRenamesHeadToIndex | GTRepositoryStatusFlagsIncludeUnmodified) }; - BOOL (^fileStatusEqualsExpected)(NSString *filename, GTStatusDeltaStatus headToIndexStatus, GTStatusDeltaStatus indexToWorkingDirectoryStatus) = ^(NSString *filename, GTStatusDeltaStatus headToIndexStatus, GTStatusDeltaStatus indexToWorkingDirectoryStatus) { + BOOL (^fileStatusEqualsExpected)(NSString *filename, GTDeltaType headToIndexStatus, GTDeltaType indexToWorkingDirectoryStatus) = ^(NSString *filename, GTDeltaType headToIndexStatus, GTDeltaType indexToWorkingDirectoryStatus) { return [index.repository enumerateFileStatusWithOptions:renamedOptions error:NULL usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { if (![headToIndex.newFile.path isEqualToString:filename]) return; - expect(headToIndex.status).to.equal(headToIndexStatus); - expect(indexToWorkingDirectory.status).to.equal(indexToWorkingDirectoryStatus); + expect(@(headToIndex.status)).to(equal(@(headToIndexStatus))); + expect(@(indexToWorkingDirectory.status)).to(equal(@(indexToWorkingDirectoryStatus))); }]; }; beforeEach(^{ - expect(filename).to.equal([filename precomposedStringWithCanonicalMapping]); + expect(filename).to(equal([filename precomposedStringWithCanonicalMapping])); repo = self.testUnicodeFixtureRepository; configuration = [repo configurationWithError:NULL]; [configuration setBool:false forKey:@"core.precomposeunicode"]; - expect([configuration boolForKey:@"core.precomposeunicode"]).to.beFalsy(); + expect(@([configuration boolForKey:@"core.precomposeunicode"])).to(beFalsy()); index = [repo indexWithError:NULL]; @@ -227,53 +227,109 @@ renamedFileURL = [NSURL fileURLWithPath:newPath isDirectory:NO]; }); + it(@"should add all files from the current file system to the index", ^{ + NSData *currentFileContent = [[NSFileManager defaultManager] contentsAtPath:fileURL.path]; + expect(currentFileContent).notTo(beNil()); + NSString *currentFileString = [[NSString alloc] initWithData:currentFileContent encoding:NSUTF8StringEncoding]; + currentFileString = [currentFileString stringByAppendingString:@"I would like to append this to the file"]; + currentFileContent = [currentFileString dataUsingEncoding:NSUTF8StringEncoding]; + expect(@([[NSFileManager defaultManager] createFileAtPath:fileURL.path contents:currentFileContent attributes:nil])).to(beTruthy()); + + NSString *newFileContent = @"This is a new file \n1 2 3"; + NSData *newFileData = [newFileContent dataUsingEncoding:NSUTF8StringEncoding]; + expect(newFileData).notTo(beNil()); + expect(@([[NSFileManager defaultManager] createFileAtPath:renamedFileURL.path contents:newFileData attributes:nil])).to(beTruthy()); + + GTIndexEntry *entry = [index entryWithPath:[filename decomposedStringWithCanonicalMapping]]; + expect(@(fileStatusEqualsExpected(entry.path, GTDeltaTypeUnmodified, GTDeltaTypeModified))).to(beTruthy()); + entry = [index entryWithPath:[renamedFilename decomposedStringWithCanonicalMapping]]; + expect(@(fileStatusEqualsExpected(entry.path, GTDeltaTypeUnmodified, GTDeltaTypeUntracked))).to(beTruthy()); + + expect(@([index addAll:NULL])).to(beTruthy()); + expect(@([index write:NULL])).to(beTruthy()); + + entry = [index entryWithPath:[filename decomposedStringWithCanonicalMapping]]; + expect(@(fileStatusEqualsExpected(entry.path, GTDeltaTypeModified, GTDeltaTypeUnmodified))).to(beTruthy()); + entry = [index entryWithPath:[renamedFilename decomposedStringWithCanonicalMapping]]; + expect(@(fileStatusEqualsExpected(entry.path, GTDeltaTypeAdded, GTDeltaTypeUnmodified))).to(beTruthy()); + }); + it(@"it preserves decomposed Unicode in index paths with precomposeunicode disabled", ^{ NSString *decomposedFilename = [filename decomposedStringWithCanonicalMapping]; - GTIndexEntry *entry = [index entryWithName:decomposedFilename error:NULL]; - expect(fileStatusEqualsExpected(entry.path, GTStatusDeltaStatusUnmodified, GTStatusDeltaStatusUnmodified)).to.beTruthy(); + GTIndexEntry *entry = [index entryWithPath:decomposedFilename error:NULL]; + expect(@(fileStatusEqualsExpected(entry.path, GTDeltaTypeUnmodified, GTDeltaTypeUnmodified))).to(beTruthy()); - expect([[NSFileManager defaultManager] moveItemAtURL:fileURL toURL:renamedFileURL error:NULL]).to.beTruthy(); + expect(@([[NSFileManager defaultManager] moveItemAtURL:fileURL toURL:renamedFileURL error:NULL])).to(beTruthy()); - entry = [index entryWithName:decomposedFilename error:NULL]; - expect(fileStatusEqualsExpected(entry.path, GTStatusDeltaStatusUnmodified, GTStatusDeltaStatusDeleted)).to.beTruthy(); + entry = [index entryWithPath:decomposedFilename error:NULL]; + expect(@(fileStatusEqualsExpected(entry.path, GTDeltaTypeUnmodified, GTDeltaTypeDeleted))).to(beTruthy()); [index removeFile:filename error:NULL]; [index addFile:renamedFilename error:NULL]; [index write:NULL]; - entry = [index entryWithName:[renamedFilename decomposedStringWithCanonicalMapping] error:NULL]; - expect(fileStatusEqualsExpected(entry.path, GTStatusDeltaStatusRenamed, GTStatusDeltaStatusUnmodified)).to.beTruthy(); + entry = [index entryWithPath:[renamedFilename decomposedStringWithCanonicalMapping] error:NULL]; + expect(@(fileStatusEqualsExpected(entry.path, GTDeltaTypeRenamed, GTDeltaTypeUnmodified))).to(beTruthy()); }); it(@"it preserves precomposed Unicode in index paths with precomposeunicode enabled", ^{ - GTIndexEntry *fileEntry = [index entryWithName:[filename decomposedStringWithCanonicalMapping] error:NULL]; - expect(fileEntry).toNot.beNil(); - expect(fileStatusEqualsExpected(fileEntry.path, GTStatusDeltaStatusUnmodified, GTStatusDeltaStatusUnmodified)).to.beTruthy(); + GTIndexEntry *fileEntry = [index entryWithPath:[filename decomposedStringWithCanonicalMapping] error:NULL]; + expect(fileEntry).notTo(beNil()); + expect(@(fileStatusEqualsExpected(fileEntry.path, GTDeltaTypeUnmodified, GTDeltaTypeUnmodified))).to(beTruthy()); [configuration setBool:true forKey:@"core.precomposeunicode"]; - expect([configuration boolForKey:@"core.precomposeunicode"]).to.beTruthy(); + expect(@([configuration boolForKey:@"core.precomposeunicode"])).to(beTruthy()); - GTIndexEntry *decomposedFileEntry = [index entryWithName:[filename decomposedStringWithCanonicalMapping] error:NULL]; - expect(decomposedFileEntry).toNot.beNil(); - expect(fileStatusEqualsExpected(decomposedFileEntry.path, GTStatusDeltaStatusUnmodified, GTStatusDeltaStatusDeleted)).to.beTruthy(); + GTIndexEntry *decomposedFileEntry = [index entryWithPath:[filename decomposedStringWithCanonicalMapping] error:NULL]; + expect(decomposedFileEntry).notTo(beNil()); + expect(@(fileStatusEqualsExpected(decomposedFileEntry.path, GTDeltaTypeUnmodified, GTDeltaTypeDeleted))).to(beTruthy()); - expect([[NSFileManager defaultManager] moveItemAtURL:fileURL toURL:renamedFileURL error:NULL]).to.beTruthy(); + expect(@([[NSFileManager defaultManager] moveItemAtURL:fileURL toURL:renamedFileURL error:NULL])).to(beTruthy()); - GTIndexEntry *precomposedFileEntry = [index entryWithName:filename error:NULL]; - expect(precomposedFileEntry).to.beNil(); + GTIndexEntry *precomposedFileEntry = [index entryWithPath:filename error:NULL]; + expect(precomposedFileEntry).to(beNil()); - decomposedFileEntry = [index entryWithName:[filename decomposedStringWithCanonicalMapping] error:NULL]; - expect(decomposedFileEntry).toNot.beNil(); - expect(fileStatusEqualsExpected(decomposedFileEntry.path, GTStatusDeltaStatusUnmodified, GTStatusDeltaStatusDeleted)).to.beTruthy(); + decomposedFileEntry = [index entryWithPath:[filename decomposedStringWithCanonicalMapping] error:NULL]; + expect(decomposedFileEntry).notTo(beNil()); + expect(@(fileStatusEqualsExpected(decomposedFileEntry.path, GTDeltaTypeUnmodified, GTDeltaTypeDeleted))).to(beTruthy()); [index removeFile:filename error:NULL]; [index addFile:renamedFilename error:NULL]; [index write:NULL]; - GTIndexEntry *precomposedRenamedFileEntry = [index entryWithName:renamedFilename error:NULL]; - expect(precomposedRenamedFileEntry).toNot.beNil(); - expect(fileStatusEqualsExpected(precomposedFileEntry.path, GTStatusDeltaStatusRenamed, GTStatusDeltaStatusUntracked)).to.beTruthy(); + GTIndexEntry *precomposedRenamedFileEntry = [index entryWithPath:renamedFilename error:NULL]; + expect(precomposedRenamedFileEntry).notTo(beNil()); + expect(@(fileStatusEqualsExpected(precomposedFileEntry.path, GTDeltaTypeRenamed, GTDeltaTypeUntracked))).to(beTruthy()); + }); +}); + +describe(@"adding data", ^{ + __block GTRepository *repo; + __block GTIndex *index; + __block NSError *error; + + beforeEach(^{ + error = nil; + repo = self.testUnicodeFixtureRepository; + // Not sure why but it doesn't work with an in memory index + // index = [GTIndex inMemoryIndexWithRepository:repo error:&error]; + index = [repo indexWithError:&error]; + expect(error).to(beNil()); }); + + it(@"should store data at given path", ^{ + NSData *data = [NSData dataWithBytes:"foo" length:4]; + [index addData:data withPath:@"bar/foo" error:&error]; + expect(error).to(beNil()); + + GTIndexEntry *entry = [index entryWithPath:@"bar/foo" error:&error]; + expect(entry).notTo(beNil()); + expect(error).to(beNil()); + }); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTNoteSpec.m b/ObjectiveGitTests/GTNoteSpec.m new file mode 100644 index 000000000..3f977df5c --- /dev/null +++ b/ObjectiveGitTests/GTNoteSpec.m @@ -0,0 +1,87 @@ +// +// GTNoteSpec.m +// ObjectiveGitFramework +// +// Created by Slava Karpenko on 2016/05/17. +// Copyright (c) 2016 Wildbit LLC. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTNoteSpec) + +__block GTRepository *repository; +__block GTCommit *initialCommit; + +beforeEach(^{ + NSURL *fileURL = [self.tempDirectoryFileURL URLByAppendingPathComponent:[[NSUUID alloc] init].UUIDString isDirectory:NO]; + repository = [GTRepository initializeEmptyRepositoryAtFileURL:fileURL options:nil error:NULL]; + expect(repository).notTo(beNil()); + + GTTreeBuilder *builder = [[GTTreeBuilder alloc] initWithTree:nil repository:repository error:NULL]; + expect(builder).notTo(beNil()); + + GTTreeEntry *entry = [builder addEntryWithData:[@"Xyzzy" dataUsingEncoding:NSUTF8StringEncoding] fileName:@"test.txt" fileMode:GTFileModeBlob error:NULL]; + expect(entry).notTo(beNil()); + + GTTree *tree = [builder writeTree:NULL]; + expect(tree).notTo(beNil()); + + initialCommit = [repository createCommitWithTree:tree message:@"Initial commit" parents:nil updatingReferenceNamed:@"refs/heads/master" error:NULL]; + expect(initialCommit).notTo(beNil()); +}); + +it(@"can create notes", ^{ + // Annotate the commit + GTSignature *sig = [repository userSignatureForNow]; + expect(sig).notTo(beNil()); + + NSError *err = nil; + + GTNote *note = [repository createNote:@"Note text" target:initialCommit referenceName:nil author:sig committer:sig overwriteIfExists:YES error:&err]; + expect(note).notTo(beNil()); + expect(err).to(beNil()); + + [repository enumerateNotesWithReferenceName:nil error:&err usingBlock:^(GTNote *note, GTObject *object, NSError *error, BOOL *stop) { + expect(error).to(beNil()); + expect(note).notTo(beNil()); + expect(object).notTo(beNil()); + + expect(note.note).to(equal(@"Note text")); + }]; + expect(err).to(beNil()); +}); + +it(@"can delete notes", ^{ + // Annotate the commit + GTSignature *sig = [repository userSignatureForNow]; + expect(sig).notTo(beNil()); + + NSError *err = nil; + + GTNote *note = [repository createNote:@"Note text" target:initialCommit referenceName:nil author:sig committer:sig overwriteIfExists:YES error:&err]; + expect(note).notTo(beNil()); + expect(err).to(beNil()); + + BOOL res = [repository removeNoteFromObject:initialCommit referenceName:nil author:sig committer:sig error:&err]; + expect(@(res)).to(beTrue()); + expect(err).to(beNil()); + + NSMutableArray *notes = [NSMutableArray arrayWithCapacity:0]; + + [repository enumerateNotesWithReferenceName:nil error:&err usingBlock:^(GTNote *note, GTObject *object, NSError *error, BOOL *stop) { + [notes addObject:note]; + }]; + + expect(@(notes.count)).to(equal(@(0))); +}); + +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTOIDSpec.m b/ObjectiveGitTests/GTOIDSpec.m index d24b64c5b..ae9421a1f 100644 --- a/ObjectiveGitTests/GTOIDSpec.m +++ b/ObjectiveGitTests/GTOIDSpec.m @@ -6,7 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -SpecBegin(GTOID) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTOIDSpec) NSString *testSHA = @"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"; @@ -15,26 +21,26 @@ beforeEach(^{ testOID = [[GTOID alloc] initWithSHA:testSHA]; - expect(testOID).notTo.beNil(); - expect(testOID.SHA).to.equal(testSHA); + expect(testOID).notTo(beNil()); + expect(testOID.SHA).to(equal(testSHA)); }); it(@"should expose the git_oid", ^{ - expect(testOID.git_oid).notTo.beNil(); - expect(testOID).to.equal([[GTOID alloc] initWithGitOid:testOID.git_oid]); + expect([NSValue valueWithPointer:testOID.git_oid]).notTo(equal([NSValue valueWithPointer:NULL])); + expect(testOID).to(equal([[GTOID alloc] initWithGitOid:testOID.git_oid])); }); it(@"should compare equal to an OID created with the same SHA", ^{ - expect(testOID).to.equal([[GTOID alloc] initWithSHA:testSHA]); + expect(testOID).to(equal([[GTOID alloc] initWithSHA:testSHA])); }); it(@"should compare unequal to a different OID", ^{ NSString *secondSHA = @"82dc47f6ba3beecab33080a1136d8913098e1801"; - expect(testOID).notTo.equal([[GTOID alloc] initWithSHA:secondSHA]); + expect(testOID).notTo(equal([[GTOID alloc] initWithSHA:secondSHA])); }); it(@"should compare equal to an OID created with the same SHA from a C string", ^{ - expect(testOID).to.equal([[GTOID alloc] initWithSHACString:"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"]); + expect(testOID).to(equal([[GTOID alloc] initWithSHACString:"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"])); }); }); @@ -42,36 +48,40 @@ const git_oid *git_oid = NULL; { - GTOID *testOID __attribute__((objc_precise_lifetime)) = [[GTOID alloc] initWithSHA:testSHA]; + GTOID *testOID = [[GTOID alloc] initWithSHA:testSHA]; git_oid = testOID.git_oid; } GTOID *testOID = [[GTOID alloc] initWithGitOid:git_oid]; - expect(testOID.SHA).to.equal(testSHA); + expect(testOID.SHA).to(equal(testSHA)); }); it(@"should return an error when initialized with an empty SHA string", ^{ NSError *error = nil; GTOID *oid = [[GTOID alloc] initWithSHA:@"" error:&error]; - expect(oid).to.beNil(); - expect(error).notTo.beNil(); + expect(oid).to(beNil()); + expect(error).notTo(beNil()); }); it(@"should return an error when initialized with a string that contains non-hex characters", ^{ NSError *error = nil; GTOID *oid = [[GTOID alloc] initWithSHA:@"zzzzz8f4404d3a388efbff6711f1bdf28ffd16a0" error:&error]; - expect(oid).to.beNil(); - expect(error).notTo.beNil(); + expect(oid).to(beNil()); + expect(error).notTo(beNil()); }); it(@"should return an error when initialized with a string shorter than 40 characters", ^{ NSError *error = nil; GTOID *oid = [[GTOID alloc] initWithSHA:@"f7ecd80" error:&error]; - expect(oid).to.beNil(); - expect(error).notTo.beNil(); + expect(oid).to(beNil()); + expect(error).notTo(beNil()); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTObjectDatabaseSpec.m b/ObjectiveGitTests/GTObjectDatabaseSpec.m index 040f6ae4c..654a06fc8 100644 --- a/ObjectiveGitTests/GTObjectDatabaseSpec.m +++ b/ObjectiveGitTests/GTObjectDatabaseSpec.m @@ -6,18 +6,22 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTObjectDatabase.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTObjectDatabase) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTObjectDatabaseSpec) __block GTObjectDatabase *database; beforeEach(^{ GTRepository *repo = self.bareFixtureRepository; - expect(repo).notTo.beNil(); + expect(repo).notTo(beNil()); database = [repo objectDatabaseWithError:NULL]; - expect(database).notTo.beNil(); + expect(database).notTo(beNil()); }); it(@"should know what objects exist", ^{ @@ -32,33 +36,33 @@ ]; for (NSString *SHA in existentSHAs) { - expect([database containsObjectWithSHA:SHA error:NULL]).to.beTruthy(); + expect(@([database containsObjectWithSHA:SHA error:NULL])).to(beTruthy()); } for (NSString *SHA in nonExistentSHAs) { - expect([database containsObjectWithSHA:SHA error:NULL]).to.beFalsy(); + expect(@([database containsObjectWithSHA:SHA error:NULL])).to(beFalsy()); } }); it(@"should be able to read an object", ^{ GTOdbObject *object = [database objectWithSHA:@"8496071c1b46c854b31185ea97743be6a8774479" error:NULL]; - expect(object).notTo.beNil(); - expect(object.type).to.equal(GTObjectTypeCommit); + expect(object).notTo(beNil()); + expect(@(object.type)).to(equal(@(GTObjectTypeCommit))); NSData *data = object.data; - expect(data).notTo.beNil(); - expect(data.length).to.equal(172); - + expect(data).notTo(beNil()); + expect(@(data.length)).to(equal(@172)); + NSString *stringContents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - expect(stringContents).notTo.beNil(); + expect(stringContents).notTo(beNil()); NSString *header = [stringContents substringToIndex:45]; - expect(header).to.equal(@"tree 181037049a54a1eb5fab404658a3a250b44335d7"); + expect(header).to(equal(@"tree 181037049a54a1eb5fab404658a3a250b44335d7")); }); it(@"shouldn't be able to read a non-existent object", ^{ GTOdbObject *object = [database objectWithSHA:@"a496071c1b46c854b31185ea97743be6a8774471" error:NULL]; - expect(object).to.beNil(); + expect(object).to(beNil()); }); it(@"should be able to write", ^{ @@ -66,8 +70,12 @@ static const GTObjectType testContentType = GTObjectTypeBlob; static NSString * const testContentSHA = @"76b1b55ab653581d6f2c7230d34098e837197674"; GTOID *oid = [database writeData:[testContent dataUsingEncoding:NSUTF8StringEncoding] type:testContentType error:NULL]; - expect(oid.SHA).to.equal(testContentSHA); - expect([database containsObjectWithSHA:testContentSHA error:NULL]).to.beTruthy(); + expect(oid.SHA).to(equal(testContentSHA)); + expect(@([database containsObjectWithSHA:testContentSHA error:NULL])).to(beTruthy()); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTObjectTest.m b/ObjectiveGitTests/GTObjectSpec.m similarity index 68% rename from ObjectiveGitTests/GTObjectTest.m rename to ObjectiveGitTests/GTObjectSpec.m index 802883edc..677b9c5d2 100644 --- a/ObjectiveGitTests/GTObjectTest.m +++ b/ObjectiveGitTests/GTObjectSpec.m @@ -27,71 +27,67 @@ // THE SOFTWARE. // -@interface GTObjectTest : GTTestCase { - - GTRepository *repo; -} -@end +@import ObjectiveGit; +@import Nimble; +@import Quick; -@implementation GTObjectTest +#import "QuickSpec+GTFixtures.h" -- (void)setUp { - repo = self.bareFixtureRepository; -} +QuickSpecBegin(GTObjectSpec) -- (void)testCanLookupEmptyStringFails { - +__block GTRepository *repo; + +beforeEach(^{ + repo = self.bareFixtureRepository; +}); + +it(@"should fail to look up an empty string", ^{ NSError *error = nil; GTObject *obj = [repo lookUpObjectBySHA:@"" error:&error]; - - STAssertNotNil(error, nil); - STAssertNil(obj, nil); + + XCTAssertNotNil(error); + XCTAssertNil(obj); NSLog(@"Error = %@", [error localizedDescription]); -} +}); -- (void)testCanLookupBadObjectFails { - +it(@"should fail to look up a bad object", ^{ NSError *error = nil; GTObject *obj = [repo lookUpObjectBySHA:@"a496071c1b46c854b31185ea97743be6a8774479" error:&error]; - - STAssertNotNil(error, nil); - STAssertNil(obj, nil); + + XCTAssertNotNil(error); + XCTAssertNil(obj); NSLog(@"Error = %@", [error localizedDescription]); -} +}); -- (void)testCanLookupAnObject { - +it(@"should look up a valid object", ^{ NSError *error = nil; GTObject *obj = [repo lookUpObjectBySHA:@"8496071c1b46c854b31185ea97743be6a8774479" error:&error]; - - STAssertNil(error, [error localizedDescription]); - STAssertNotNil(obj, nil); - STAssertEqualObjects(obj.type, @"commit", nil); - STAssertEqualObjects(obj.SHA, @"8496071c1b46c854b31185ea97743be6a8774479", nil); -} - -- (void)testTwoObjectsAreTheSame { - + + XCTAssertNil(error, "%@", error.localizedDescription); + XCTAssertNotNil(obj); + XCTAssertEqualObjects(obj.type, @"commit"); + XCTAssertEqualObjects(obj.SHA, @"8496071c1b46c854b31185ea97743be6a8774479"); +}); + +it(@"should look up equivalent objects", ^{ NSError *error = nil; GTObject *obj1 = [repo lookUpObjectBySHA:@"8496071c1b46c854b31185ea97743be6a8774479" error:&error]; GTObject *obj2 = [repo lookUpObjectBySHA:@"8496071c1b46c854b31185ea97743be6a8774479" error:&error]; - - STAssertNotNil(obj1, nil); - STAssertNotNil(obj2, nil); - STAssertTrue([obj1 isEqual:obj2], nil); -} - -- (void)testCanReadRawDataFromObject { - + + XCTAssertNotNil(obj1); + XCTAssertNotNil(obj2); + XCTAssertTrue([obj1 isEqual:obj2]); +}); + +it(@"should read the raw data from an object", ^{ NSError *error = nil; GTObject *obj = [repo lookUpObjectBySHA:@"8496071c1b46c854b31185ea97743be6a8774479" error:&error]; - - STAssertNotNil(obj, nil); - + + XCTAssertNotNil(obj); + GTOdbObject *rawObj = [obj odbObjectWithError:&error]; - STAssertNotNil(rawObj, nil); - STAssertNil(error, [error localizedDescription]); - NSLog(@"rawObj len = %ld", [rawObj.data length]); -} + XCTAssertNotNil(rawObj); + XCTAssertNil(error, @"%@", error.localizedDescription); +}); -@end +QuickSpecEnd diff --git a/ObjectiveGitTests/GTReferenceSpec.m b/ObjectiveGitTests/GTReferenceSpec.m index 3db01eeb5..a3b9b4022 100644 --- a/ObjectiveGitTests/GTReferenceSpec.m +++ b/ObjectiveGitTests/GTReferenceSpec.m @@ -6,42 +6,48 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -SpecBegin(GTReference) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTReferenceSpec) __block GTRepository *repository; beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); }); it(@"should compare equal to the same reference", ^{ - expect([[GTReference alloc] initByLookingUpReferenceNamed:@"refs/heads/master" inRepository:repository error:NULL]).to.equal([[GTReference alloc] initByLookingUpReferenceNamed:@"refs/heads/master" inRepository:repository error:NULL]); + expect([repository lookUpReferenceWithName:@"refs/heads/master" error:NULL]).to(equal([repository lookUpReferenceWithName:@"refs/heads/master" error:NULL])); }); it(@"should compare unequal to a different reference", ^{ - expect([[GTReference alloc] initByLookingUpReferenceNamed:@"refs/heads/master" inRepository:repository error:NULL]).notTo.equal([[GTReference alloc] initByLookingUpReferenceNamed:@"refs/remotes/origin/master" inRepository:repository error:NULL]); + expect([repository lookUpReferenceWithName:@"refs/heads/master" error:NULL]).notTo(equal([repository lookUpReferenceWithName:@"refs/remotes/origin/master" error:NULL])); }); describe(@"remote property", ^{ it(@"should be YES for a remote-tracking branch", ^{ NSError *error = nil; - GTReference *ref = [[GTReference alloc] initByLookingUpReferenceNamed:@"refs/remotes/origin/master" inRepository:repository error:&error]; - expect(ref).notTo.beNil(); - expect(error).to.beNil(); + GTReference *ref = [repository lookUpReferenceWithName:@"refs/remotes/origin/master" error:&error]; + expect(ref).notTo(beNil()); + expect(error).to(beNil()); - expect(ref.OID.SHA).to.equal(@"d603d61ea756eb881ba440b3e66b561d070aec6e"); - expect(ref.remote).to.beTruthy(); + expect(ref.OID.SHA).to(equal(@"d603d61ea756eb881ba440b3e66b561d070aec6e")); + expect(@(ref.remote)).to(beTruthy()); }); it(@"should be NO for a local branch", ^{ NSError *error = nil; - GTReference *ref = [[GTReference alloc] initByLookingUpReferenceNamed:@"refs/heads/master" inRepository:repository error:&error]; - expect(ref).notTo.beNil(); - expect(error).to.beNil(); + GTReference *ref = [repository lookUpReferenceWithName:@"refs/heads/master" error:&error]; + expect(ref).notTo(beNil()); + expect(error).to(beNil()); - expect(ref.OID.SHA).to.equal(@"a4bca6b67a5483169963572ee3da563da33712f7"); - expect(ref.remote).to.beFalsy(); + expect(ref.OID.SHA).to(equal(@"a4bca6b67a5483169963572ee3da563da33712f7")); + expect(@(ref.remote)).to(beFalsy()); }); }); @@ -53,69 +59,69 @@ beforeEach(^{ GTRepository *repository = self.bareFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); NSError *error; - reference = [repository createReferenceNamed:testRefName fromOID:testRefOID committer:nil message:nil error:&error]; - expect(reference).notTo.beNil(); - expect(reference.name).to.equal(testRefName); - expect(reference.targetSHA).to.equal(testRefOID.SHA); + reference = [repository createReferenceNamed:testRefName fromOID:testRefOID message:nil error:&error]; + expect(reference).notTo(beNil()); + expect(reference.name).to(equal(testRefName)); + expect(reference.targetOID).to(equal(testRefOID)); }); it(@"should be able to be renamed", ^{ NSString *newRefName = @"refs/heads/new_name"; GTReference *renamedRef = [reference referenceByRenaming:newRefName error:NULL]; - expect(renamedRef).notTo.beNil(); - expect(renamedRef.name).to.equal(newRefName); - expect(renamedRef.targetSHA).to.equal(testRefOID.SHA); + expect(renamedRef).notTo(beNil()); + expect(renamedRef.name).to(equal(newRefName)); + expect(renamedRef.targetOID).to(equal(testRefOID)); }); it(@"should be able to change the target", ^{ NSString *newRefTarget = @"5b5b025afb0b4c913b4c338a42934a3863bf3644"; - GTReference *updatedRef = [reference referenceByUpdatingTarget:newRefTarget committer:nil message:nil error:NULL]; - expect(updatedRef).notTo.beNil(); - expect(updatedRef.name).to.equal(testRefName); - expect(updatedRef.targetSHA).to.equal(newRefTarget); + GTReference *updatedRef = [reference referenceByUpdatingTarget:newRefTarget message:nil error:NULL]; + expect(updatedRef).notTo(beNil()); + expect(updatedRef.name).to(equal(testRefName)); + expect(updatedRef.targetOID.SHA).to(equal(newRefTarget)); }); }); describe(@"valid names",^{ it(@"should accept uppercase top-level names", ^{ - expect([GTReference isValidReferenceName:@"HEAD"]).to.beTruthy(); - expect([GTReference isValidReferenceName:@"ORIG_HEAD"]).to.beTruthy(); + expect(@([GTReference isValidReferenceName:@"HEAD"])).to(beTruthy()); + expect(@([GTReference isValidReferenceName:@"ORIG_HEAD"])).to(beTruthy()); }); it(@"should not accept lowercase top-level names",^{ - expect([GTReference isValidReferenceName:@"head"]).notTo.beTruthy(); + expect(@([GTReference isValidReferenceName:@"head"])).notTo(beTruthy()); }); it(@"should accept names with the refs/ prefix",^{ - expect([GTReference isValidReferenceName:@"refs/stuff"]).to.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/multiple/components"]).to.beTruthy(); + expect(@([GTReference isValidReferenceName:@"refs/stuff"])).to(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/multiple/components"])).to(beTruthy()); }); it(@"should not accept names with invalid parts",^{ - expect([GTReference isValidReferenceName:@"refs/stuff~"]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff^"]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff:"]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff\\"]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff?"]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff["]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff*"]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff.."]).notTo.beTruthy(); - expect([GTReference isValidReferenceName:@"refs/stuff@{"]).notTo.beTruthy(); + expect(@([GTReference isValidReferenceName:@"refs/stuff~"])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff^"])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff:"])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff\\"])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff?"])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff["])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff*"])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff.."])).notTo(beTruthy()); + expect(@([GTReference isValidReferenceName:@"refs/stuff@{"])).notTo(beTruthy()); }); }); __block GTRepository *bareRepository; void (^expectValidReference)(GTReference *ref, NSString *SHA, GTReferenceType type, NSString *name) = ^(GTReference *ref, NSString *SHA, GTReferenceType type, NSString *name) { - expect(ref).notTo.beNil(); - expect(ref.targetSHA).to.equal(SHA); - expect(ref.referenceType).to.equal(type); - expect(ref.name).to.equal(name); + expect(ref).notTo(beNil()); + expect(ref.targetOID.SHA).to(equal(SHA)); + expect(@(ref.referenceType)).to(equal(@(type))); + expect(ref.name).to(equal(name)); }; beforeEach(^{ @@ -125,46 +131,46 @@ describe(@"+referenceByLookingUpReferenceNamed:inRepository:error:", ^{ it(@"should return a valid reference to a branch", ^{ NSError *error = nil; - GTReference *ref = [GTReference referenceByLookingUpReferencedNamed:@"refs/heads/master" inRepository:bareRepository error:&error]; - expect(ref).notTo.beNil(); - expect(error).to.beNil(); + GTReference *ref = [bareRepository lookUpReferenceWithName:@"refs/heads/master" error:&error]; + expect(ref).notTo(beNil()); + expect(error).to(beNil()); - expectValidReference(ref, @"36060c58702ed4c2a40832c51758d5344201d89a", GTReferenceTypeOid, @"refs/heads/master"); + expectValidReference(ref, @"36060c58702ed4c2a40832c51758d5344201d89a", GTReferenceTypeDirect, @"refs/heads/master"); }); it(@"should return a valid reference to a tag", ^{ NSError *error = nil; - GTReference *ref = [GTReference referenceByLookingUpReferencedNamed:@"refs/tags/v0.9" inRepository:bareRepository error:&error]; - expect(ref).notTo.beNil(); - expect(error).to.beNil(); + GTReference *ref = [bareRepository lookUpReferenceWithName:@"refs/tags/v0.9" error:&error]; + expect(ref).notTo(beNil()); + expect(error).to(beNil()); - expectValidReference(ref, @"5b5b025afb0b4c913b4c338a42934a3863bf3644", GTReferenceTypeOid, @"refs/tags/v0.9"); + expectValidReference(ref, @"5b5b025afb0b4c913b4c338a42934a3863bf3644", GTReferenceTypeDirect, @"refs/tags/v0.9"); }); }); describe(@"creating", ^{ it(@"can create a reference from a symbolic reference", ^{ - GTReference *target = [[GTReference alloc] initByLookingUpReferenceNamed:@"refs/heads/master" inRepository:bareRepository error:NULL]; - expect(target).notTo.beNil(); + GTReference *target = [bareRepository lookUpReferenceWithName:@"refs/heads/master" error:NULL]; + expect(target).notTo(beNil()); NSError *error = nil; - GTReference *ref = [bareRepository createReferenceNamed:@"refs/heads/unit_test" fromReference:target committer:nil message:nil error:&error]; - expect(error).to.beNil(); - expect(ref).notTo.beNil(); + GTReference *ref = [bareRepository createReferenceNamed:@"refs/heads/unit_test" fromReference:target message:nil error:&error]; + expect(error).to(beNil()); + expect(ref).notTo(beNil()); expectValidReference(ref, @"36060c58702ed4c2a40832c51758d5344201d89a", GTReferenceTypeSymbolic, @"refs/heads/unit_test"); - expect(ref.resolvedReference.name).to.equal(@"refs/heads/master"); + expect(ref.resolvedReference.name).to(equal(@"refs/heads/master")); }); it(@"can create a reference from an SHA/OID", ^{ GTOID *target = [[GTOID alloc] initWithSHA:@"36060c58702ed4c2a40832c51758d5344201d89a"]; NSError *error = nil; - GTReference *ref = [bareRepository createReferenceNamed:@"refs/heads/unit_test" fromOID:target committer:nil message:nil error:&error]; - expect(error).to.beNil(); - expect(ref).notTo.beNil(); + GTReference *ref = [bareRepository createReferenceNamed:@"refs/heads/unit_test" fromOID:target message:nil error:&error]; + expect(error).to(beNil()); + expect(ref).notTo(beNil()); - expectValidReference(ref, @"36060c58702ed4c2a40832c51758d5344201d89a", GTReferenceTypeOid, @"refs/heads/unit_test"); + expectValidReference(ref, @"36060c58702ed4c2a40832c51758d5344201d89a", GTReferenceTypeDirect, @"refs/heads/unit_test"); }); }); @@ -173,15 +179,19 @@ GTOID *target = [[GTOID alloc] initWithSHA:@"36060c58702ed4c2a40832c51758d5344201d89a"]; NSError *error = nil; - GTReference *ref = [bareRepository createReferenceNamed:@"refs/heads/unit_test" fromOID:target committer:nil message:nil error:&error]; + GTReference *ref = [bareRepository createReferenceNamed:@"refs/heads/unit_test" fromOID:target message:nil error:&error]; - expect(error).to.beNil(); - expect(ref).notTo.beNil(); + expect(error).to(beNil()); + expect(ref).notTo(beNil()); BOOL success = [ref deleteWithError:&error]; - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTReflogSpec.m b/ObjectiveGitTests/GTReflogSpec.m index 374aa2b27..cc4bd022b 100644 --- a/ObjectiveGitTests/GTReflogSpec.m +++ b/ObjectiveGitTests/GTReflogSpec.m @@ -6,32 +6,36 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTReflog.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTReflog) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTReflogSpec) __block GTReflog *reflog; __block GTRepository *repository; beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); GTBranch *branch = [repository currentBranchWithError:NULL]; - expect(branch).notTo.beNil(); - + expect(branch).notTo(beNil()); + reflog = branch.reference.reflog; - expect(reflog).notTo.beNil(); + expect(reflog).notTo(beNil()); }); describe(@"reading", ^{ it(@"should be able to read reflog entries", ^{ GTReflogEntry *entry = [reflog entryAtIndex:0]; - expect(entry).notTo.beNil(); - expect(entry.message).to.equal(@"commit: Add 2 text and 1 binary file for diff tests."); - expect(entry.previousOID).to.equal([[GTOID alloc] initWithSHA:@"6b0c1c8b8816416089c534e474f4c692a76ac14f"]); - expect(entry.updatedOID).to.equal([[GTOID alloc] initWithSHA:@"a4bca6b67a5483169963572ee3da563da33712f7"]); - expect(entry.committer.name).to.equal(@"Danny Greg"); - expect(entry.committer.email).to.equal(@"danny@github.com"); + expect(entry).notTo(beNil()); + expect(entry.message).to(equal(@"commit: Add 2 text and 1 binary file for diff tests.")); + expect(entry.previousOID).to(equal([[GTOID alloc] initWithSHA:@"6b0c1c8b8816416089c534e474f4c692a76ac14f"])); + expect(entry.updatedOID).to(equal([[GTOID alloc] initWithSHA:@"a4bca6b67a5483169963572ee3da563da33712f7"])); + expect(entry.committer.name).to(equal(@"Danny Greg")); + expect(entry.committer.email).to(equal(@"danny@github.com")); }); }); @@ -40,16 +44,20 @@ static NSString * const message = @"Refloggin' ain't easy."; GTSignature *user = repository.userSignatureForNow; BOOL success = [reflog writeEntryWithCommitter:user message:message error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); GTReflogEntry *entry = [reflog entryAtIndex:0]; - expect(entry).notTo.beNil(); - expect(entry.message).to.equal(message); - expect(entry.previousOID).to.equal([[GTOID alloc] initWithSHA:@"a4bca6b67a5483169963572ee3da563da33712f7"]); - expect(entry.updatedOID).to.equal([[GTOID alloc] initWithSHA:@"a4bca6b67a5483169963572ee3da563da33712f7"]); - expect(entry.committer.name).to.equal(user.name); - expect(entry.committer.email).to.equal(user.email); + expect(entry).notTo(beNil()); + expect(entry.message).to(equal(message)); + expect(entry.previousOID).to(equal([[GTOID alloc] initWithSHA:@"a4bca6b67a5483169963572ee3da563da33712f7"])); + expect(entry.updatedOID).to(equal([[GTOID alloc] initWithSHA:@"a4bca6b67a5483169963572ee3da563da33712f7"])); + expect(entry.committer.name).to(equal(user.name)); + expect(entry.committer.email).to(equal(user.email)); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRemotePushSpec.m b/ObjectiveGitTests/GTRemotePushSpec.m new file mode 100644 index 000000000..a4c4af331 --- /dev/null +++ b/ObjectiveGitTests/GTRemotePushSpec.m @@ -0,0 +1,171 @@ +// +// GTRemotePushSpec.m +// ObjectiveGitFramework +// +// Created by Ben Chatelain on 11/14/2014. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" +#import "GTUtilityFunctions.h" + +#pragma mark - GTRemotePushSpec + +QuickSpecBegin(GTRemotePushSpec) + +describe(@"pushing", ^{ + __block GTRepository *notBareRepo; + + beforeEach(^{ + notBareRepo = self.bareFixtureRepository; + expect(notBareRepo).notTo(beNil()); + // This repo is not really "bare" according to libgit2 + expect(@(notBareRepo.isBare)).to(beFalsy()); + }); + + describe(@"to remote", ^{ // via local transport + __block NSURL *remoteRepoURL; + __block NSURL *localRepoURL; + __block GTRepository *remoteRepo; + __block GTRepository *localRepo; + __block GTRemote *remote; + __block NSError *error; + + beforeEach(^{ + // Make a bare clone to serve as the remote + remoteRepoURL = [notBareRepo.gitDirectoryURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@"bare_remote_repo.git"]; + NSDictionary *options = @{ GTRepositoryCloneOptionsBare: @1 }; + remoteRepo = [GTRepository cloneFromURL:notBareRepo.gitDirectoryURL toWorkingDirectory:remoteRepoURL options:options error:&error transferProgressBlock:NULL]; + expect(error).to(beNil()); + expect(remoteRepo).notTo(beNil()); + expect(@(remoteRepo.isBare)).to(beTruthy()); // that's better + + localRepoURL = [remoteRepoURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@"local_push_repo"]; + expect(localRepoURL).notTo(beNil()); + + // Local clone for testing pushes + localRepo = [GTRepository cloneFromURL:remoteRepoURL toWorkingDirectory:localRepoURL options:nil error:&error transferProgressBlock:NULL]; + + expect(error).to(beNil()); + expect(localRepo).notTo(beNil()); + + GTConfiguration *configuration = [localRepo configurationWithError:&error]; + expect(error).to(beNil()); + expect(configuration).notTo(beNil()); + + expect(@(configuration.remotes.count)).to(equal(@1)); + + remote = configuration.remotes[0]; + expect(remote.name).to(equal(@"origin")); + }); + + afterEach(^{ + [NSFileManager.defaultManager removeItemAtURL:remoteRepoURL error:&error]; + expect(error).to(beNil()); + [NSFileManager.defaultManager removeItemAtURL:localRepoURL error:&error]; + expect(error).to(beNil()); + error = NULL; + }); + + context(@"when the local and remote branches are in sync", ^{ + it(@"should push no commits", ^{ + GTBranch *masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + GTBranch *remoteMasterBranch = localBranchWithName(@"master", remoteRepo); + expect(@([remoteMasterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Push + __block BOOL transferProgressed = NO; + BOOL result = [localRepo pushBranch:masterBranch toRemote:remote withOptions:nil error:&error progress:^(unsigned int current, unsigned int total, size_t bytes, BOOL *stop) { + transferProgressed = YES; + }]; + expect(error).to(beNil()); + expect(@(result)).to(beTruthy()); + expect(@(transferProgressed)).to(beTruthy()); + + // Same number of commits after push, refresh branch first + remoteMasterBranch = localBranchWithName(@"master", remoteRepo); + expect(@([remoteMasterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + }); + }); + + it(@"can push one commit", ^{ + // Create a new commit in the local repo + NSString *testData = @"Test"; + NSString *fileName = @"test.txt"; + GTCommit *testCommit = createCommitInRepository(@"Test commit", [testData dataUsingEncoding:NSUTF8StringEncoding], fileName, localRepo); + expect(testCommit).notTo(beNil()); + + // Refetch master branch to ensure the commit count is accurate + GTBranch *masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@4)); + + // Number of commits on tracking branch before push + BOOL success = NO; + GTBranch *localTrackingBranch = [masterBranch trackingBranchWithError:&error success:&success]; + expect(error).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(@([localTrackingBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Number of commits on remote before push + GTBranch *remoteMasterBranch = localBranchWithName(@"master", remoteRepo); + expect(@([remoteMasterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Push + __block BOOL transferProgressed = NO; + BOOL result = [localRepo pushBranch:masterBranch toRemote:remote withOptions:nil error:&error progress:^(unsigned int current, unsigned int total, size_t bytes, BOOL *stop) { + transferProgressed = YES; + }]; + expect(error).to(beNil()); + expect(@(result)).to(beTruthy()); + expect(@(transferProgressed)).to(beTruthy()); + + // Number of commits on tracking branch after push + localTrackingBranch = [masterBranch trackingBranchWithError:&error success:&success]; + expect(error).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(@([localTrackingBranch numberOfCommitsWithError:NULL])).to(equal(@4)); + + // Refresh remote master branch to ensure the commit count is accurate + remoteMasterBranch = localBranchWithName(@"master", remoteRepo); + + // Number of commits in remote repo after push + expect(@([remoteMasterBranch numberOfCommitsWithError:NULL])).to(equal(@4)); + + // Verify commit is in remote + GTCommit *pushedCommit = [remoteRepo lookUpObjectByOID:testCommit.OID objectType:GTObjectTypeCommit error:&error]; + expect(error).to(beNil()); + expect(pushedCommit).notTo(beNil()); + expect(pushedCommit.OID).to(equal(testCommit.OID)); + + GTTreeEntry *entry = [[pushedCommit tree] entryWithName:fileName]; + expect(entry).notTo(beNil()); + + GTBlob *fileData = (GTBlob *)[entry GTObject:&error]; + expect(error).to(beNil()); + expect(fileData).notTo(beNil()); + expect(fileData.content).to(equal(testData)); + }); + + it(@"can push two branches", ^{ + // refs/heads/master on local + GTBranch *branch1 = localBranchWithName(@"master", localRepo); + + // Create refs/heads/new_master on local + [localRepo createReferenceNamed:@"refs/heads/new_master" fromReference:branch1.reference message:@"Create new_master branch" error:&error]; + GTBranch *branch2 = localBranchWithName(@"new_master", localRepo); + + BOOL result = [localRepo pushBranches:@[ branch1, branch2 ] toRemote:remote withOptions:nil error:&error progress:NULL]; + expect(error).to(beNil()); + expect(@(result)).to(beTruthy()); + }); + }); + +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRemoteSpec.m b/ObjectiveGitTests/GTRemoteSpec.m index 6e5ef6584..e7ac77891 100644 --- a/ObjectiveGitTests/GTRemoteSpec.m +++ b/ObjectiveGitTests/GTRemoteSpec.m @@ -6,63 +6,202 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTRemote.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTRemote) +#import "QuickSpec+GTFixtures.h" +#import "GTUtilityFunctions.h" + +QuickSpecBegin(GTRemoteSpec) __block GTRemote *remote = nil; __block GTRepository *repository = nil; +__block GTConfiguration *configuration = nil; NSString *fetchRefspec = @"+refs/heads/*:refs/remotes/origin/*"; beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); NSError *error = nil; - GTConfiguration *configuration = [repository configurationWithError:&error]; - expect(configuration).toNot.beNil(); - expect(error).to.beNil(); + configuration = [repository configurationWithError:&error]; + expect(configuration).notTo(beNil()); + expect(error).to(beNil()); - expect(configuration.remotes.count).to.beGreaterThanOrEqualTo(1); + expect(@(configuration.remotes.count)).to(beGreaterThanOrEqualTo(@1)); remote = configuration.remotes[0]; - expect(remote.name).to.equal(@"origin"); + expect(remote.name).to(equal(@"origin")); }); describe(@"properties", ^{ it(@"should have values", ^{ - expect(remote.git_remote).toNot.beNil(); - expect(remote.name).to.equal(@"origin"); - expect(remote.URLString).to.equal(@"git@github.com:github/Test_App.git"); + expect([NSValue valueWithPointer:remote.git_remote]).notTo(equal([NSValue valueWithPointer:NULL])); + expect(remote.name).to(equal(@"origin")); + expect(remote.URLString).to(equal(@"git@github.com:github/Test_App.git")); - expect(remote.fetchRefspecs).to.equal(@[ fetchRefspec ]); + expect(remote.fetchRefspecs).to(equal(@[ fetchRefspec ])); }); }); describe(@"updating", ^{ it(@"URL string", ^{ - expect(remote.URLString).to.equal(@"git@github.com:github/Test_App.git"); + expect(remote.URLString).to(equal(@"git@github.com:github/Test_App.git")); NSString *newURLString = @"https://github.com/github/Test_App.git"; __block NSError *error = nil; - expect([remote updateURLString:newURLString error:&error]).to.beTruthy(); - expect(error).to.beNil(); + expect(@([remote updateURLString:newURLString error:&error])).to(beTruthy()); + expect(error).to(beNil()); + + // Reload remote from disk to pick up the change + remote = configuration.remotes[0]; + + expect(remote.URLString).to(equal(newURLString)); + }); - expect(remote.URLString).to.equal(newURLString); + it(@"push URL string", ^{ + expect(remote.pushURLString).to(beNil()); + + NSString *newURLString = @"https://github.com/github/Test_App.git"; + + __block NSError *error = nil; + expect(@([remote updatePushURLString:newURLString error:&error])).to(beTruthy()); + expect(error).to(beNil()); + + // Reload remote from disk to pick up the change + remote = configuration.remotes[0]; + + expect(remote.pushURLString).to(equal(newURLString)); }); it(@"fetch refspecs", ^{ - expect(remote.fetchRefspecs).to.equal(@[ fetchRefspec ]); + expect(remote.fetchRefspecs).to(equal(@[ fetchRefspec ])); NSString *newFetchRefspec = @"+refs/heads/master:refs/remotes/origin/master"; - + __block NSError *error = nil; - expect([remote addFetchRefspec:newFetchRefspec error:&error]).to.beTruthy(); - expect(error).to.beNil(); + expect(@([remote addFetchRefspec:newFetchRefspec error:&error])).to(beTruthy()); + expect(error).to(beNil()); + + // Reload remote from disk to pick up the change + remote = configuration.remotes[0]; + + expect(remote.fetchRefspecs).to(equal((@[ fetchRefspec, newFetchRefspec ]))); + }); +}); + +describe(@"network operations", ^{ + __block NSURL *repositoryURL; + __block NSURL *fetchingRepoURL; + __block GTRepository *fetchingRepo; + __block NSArray *remoteNames; + __block NSString *remoteName; + + beforeEach(^{ + repository = self.bareFixtureRepository; + expect(@(repository.isBare)).to(beFalsy()); // yeah right + repositoryURL = repository.gitDirectoryURL; + NSURL *fixturesURL = repositoryURL.URLByDeletingLastPathComponent; + fetchingRepoURL = [fixturesURL URLByAppendingPathComponent:@"fetchrepo"]; + + NSError *error = nil; + fetchingRepo = [GTRepository cloneFromURL:repositoryURL toWorkingDirectory:fetchingRepoURL options:nil error:&error transferProgressBlock:nil]; + expect(fetchingRepo).notTo(beNil()); + expect(error).to(beNil()); + + remoteNames = [fetchingRepo remoteNamesWithError:&error]; + expect(error).to(beNil()); + expect(@(remoteNames.count)).to(beGreaterThanOrEqualTo(@1)); + + remoteName = remoteNames[0]; + }); + + afterEach(^{ + [NSFileManager.defaultManager removeItemAtURL:fetchingRepoURL error:NULL]; + }); + + describe(@"-remoteWithName:inRepository:error", ^{ + it(@"should return existing remotes", ^{ + NSError *error = nil; + + GTRemote *originRemote = [GTRemote remoteWithName:remoteName inRepository:fetchingRepo error:&error]; + expect(error).to(beNil()); + expect(originRemote).notTo(beNil()); + expect(originRemote.name).to(equal(@"origin")); + expect(originRemote.URLString).to(equal(repositoryURL.path)); + }); + + it(@"should fail for non-existent remotes", ^{ + NSError *error = nil; + + GTRemote *originRemote = [GTRemote remoteWithName:@"blork" inRepository:fetchingRepo error:&error]; + expect(error).notTo(beNil()); + expect(originRemote).to(beNil()); + }); + }); + + describe(@"-createRemoteWithName:url:inRepository:error", ^{ + it(@"should allow creating new remotes", ^{ + NSError *error = nil; + GTRemote *remote = [GTRemote createRemoteWithName:@"newremote" URLString:@"git://user@example.com/testrepo.git" inRepository:fetchingRepo error:&error]; + expect(error).to(beNil()); + expect(remote).notTo(beNil()); + + GTRemote *newRemote = [GTRemote remoteWithName:@"newremote" inRepository:fetchingRepo error:&error]; + expect(error).to(beNil()); + expect(newRemote).notTo(beNil()); + expect(newRemote.URLString).to(equal(@"git://user@example.com/testrepo.git")); + }); + }); - expect(remote.fetchRefspecs).to.equal((@[ fetchRefspec, newFetchRefspec ])); + describe(@"-[GTRepository fetchRemote:withOptions:error:progress:]", ^{ + it(@"allows remotes to be fetched", ^{ + NSError *error = nil; + GTRemote *remote = [GTRemote remoteWithName:remoteName inRepository:fetchingRepo error:nil]; // Tested above + + BOOL result = [fetchingRepo fetchRemote:remote withOptions:nil error:&error progress:nil]; + expect(error).to(beNil()); + expect(@(result)).to(beTruthy()); + }); + + it(@"brings in new commits", ^{ + NSError *error = nil; + + // Create a new commit in the master repo + NSString *testData = @"Test"; + NSString *fileName = @"test.txt"; + + GTCommit *testCommit = createCommitInRepository(@"Test commit", [testData dataUsingEncoding:NSUTF8StringEncoding], fileName, repository); + + // Now issue a fetch from the fetching repo + GTRemote *remote = [GTRemote remoteWithName:remoteName inRepository:fetchingRepo error:nil]; + + __block unsigned int receivedObjects = 0; + __block BOOL transferProgressed = NO; + BOOL success = [fetchingRepo fetchRemote:remote withOptions:nil error:&error progress:^(const git_transfer_progress *stats, BOOL *stop) { + receivedObjects += stats->received_objects; + transferProgressed = YES; + }]; + expect(error).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(@(transferProgressed)).to(beTruthy()); + expect(@(receivedObjects)).to(beGreaterThan(@0)); + + GTCommit *fetchedCommit = [fetchingRepo lookUpObjectByOID:testCommit.OID objectType:GTObjectTypeCommit error:&error]; + expect(error).to(beNil()); + expect(fetchedCommit).notTo(beNil()); + + GTTreeEntry *entry = [[fetchedCommit tree] entryWithName:fileName]; + expect(entry).notTo(beNil()); + + GTBlob *fileData = (GTBlob *)[entry GTObject:&error]; + expect(error).to(beNil()); + expect(fileData).notTo(beNil()); + expect(fileData.content).to(equal(testData)); + }); }); }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRepository+PullSpec.m b/ObjectiveGitTests/GTRepository+PullSpec.m new file mode 100644 index 000000000..3c36ebde6 --- /dev/null +++ b/ObjectiveGitTests/GTRepository+PullSpec.m @@ -0,0 +1,272 @@ +// +// GTRepository+PullSpec.m +// ObjectiveGitFramework +// +// Created by Ben Chatelain on 6/28/15. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" +#import "GTUtilityFunctions.h" + +#pragma mark - GTRepository+PullSpec + +QuickSpecBegin(GTRepositoryPullSpec) + +describe(@"pull", ^{ + __block GTRepository *notBareRepo; + + beforeEach(^{ + notBareRepo = self.bareFixtureRepository; + expect(notBareRepo).notTo(beNil()); + // This repo is not really "bare" according to libgit2 + expect(@(notBareRepo.isBare)).to(beFalsy()); + }); + + describe(@"from remote", ^{ // via local transport + __block NSURL *remoteRepoURL; + __block NSURL *localRepoURL; + __block GTRepository *remoteRepo; + __block GTRepository *localRepo; + __block GTRemote *remote; + __block NSError *error; + + beforeEach(^{ + // Make a bare clone to serve as the remote + remoteRepoURL = [notBareRepo.gitDirectoryURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@"bare_remote_repo.git"]; + NSDictionary *options = @{ GTRepositoryCloneOptionsBare: @1 }; + remoteRepo = [GTRepository cloneFromURL:notBareRepo.gitDirectoryURL toWorkingDirectory:remoteRepoURL options:options error:&error transferProgressBlock:NULL]; + expect(error).to(beNil()); + expect(remoteRepo).notTo(beNil()); + expect(@(remoteRepo.isBare)).to(beTruthy()); // that's better + + localRepoURL = [remoteRepoURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@"local_pull_repo"]; + expect(localRepoURL).notTo(beNil()); + + // Local clone for testing pushes + localRepo = [GTRepository cloneFromURL:remoteRepoURL toWorkingDirectory:localRepoURL options:nil error:&error transferProgressBlock:NULL]; + + expect(error).to(beNil()); + expect(localRepo).notTo(beNil()); + + GTConfiguration *configuration = [localRepo configurationWithError:&error]; + expect(error).to(beNil()); + expect(configuration).notTo(beNil()); + + expect(@(configuration.remotes.count)).to(equal(@1)); + + remote = configuration.remotes[0]; + expect(remote.name).to(equal(@"origin")); + }); + + afterEach(^{ + [NSFileManager.defaultManager removeItemAtURL:remoteRepoURL error:NULL]; + [NSFileManager.defaultManager removeItemAtURL:localRepoURL error:NULL]; + error = NULL; + }); + + context(@"when the local and remote branches are in sync", ^{ + it(@"should pull no commits", ^{ + GTBranch *masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + GTBranch *remoteMasterBranch = localBranchWithName(@"master", remoteRepo); + expect(@([remoteMasterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Pull + __block BOOL transferProgressed = NO; + BOOL result = [localRepo pullBranch:masterBranch fromRemote:remote withOptions:nil error:&error progress:^(const git_transfer_progress *progress, BOOL *stop) { + transferProgressed = YES; + }]; + expect(error).to(beNil()); + expect(@(result)).to(beTruthy()); + expect(@(transferProgressed)).to(beFalsy()); // Local transport doesn't currently call progress callbacks + + // Same number of commits after pull, refresh branch from disk first + remoteMasterBranch = localBranchWithName(@"master", remoteRepo); + expect(@([remoteMasterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + }); + }); + +/* pending tests break build on travis + /// Unborn + /// Can't get a GTBranch reference wrapping HEAD when its symref is unborn + pending(@"into an empty repo", ^{ + // Create an empty local repo + localRepoURL = [remoteRepoURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@"empty_pull_repo"]; + NSLog(@"localRepoURL: %@", localRepoURL); + NSDictionary *options = @{ GTRepositoryInitOptionsOriginURLString: [remoteRepoURL absoluteString] }; + localRepo = [GTRepository initializeEmptyRepositoryAtFileURL:localRepoURL options:options error:&error]; + expect(localRepo).toNot(beNil()); + expect(error).to(beNil()); + + // Verify unborn + expect(@(localRepo.isHEADUnborn)).to(beTruthy()); + + // Configure tracking + GTConfiguration *configuration = [localRepo configurationWithError:&error]; + expect(configuration).toNot(beNil()); + expect(error).to(beNil()); + [configuration setString:@"origin" forKey:@"branch.master.remote"]; + [configuration setString:@"refs/heads/master" forKey:@"branch.master.merge"]; + + GTReference *head = [localRepo headReferenceWithError:&error]; + expect(head).toNot(beNil()); + expect(error).to(beNil()); + +// GTBranch *masterBranch = localBranchWithName(@"master", localRepo); + GTBranch *masterBranch = [localRepo currentBranchWithError:&error]; + expect(masterBranch).toNot(beNil()); + + // Pull + __block BOOL transferProgressed = NO; + BOOL result = [localRepo pullBranch:masterBranch fromRemote:remote withOptions:nil error:&error progress:^(const git_transfer_progress *progress, BOOL *stop) { + transferProgressed = YES; + }]; + expect(@(result)).to(beTruthy()); + expect(error).to(beNil()); + expect(@(transferProgressed)).to(beFalsy()); // Local transport doesn't currently call progress callbacks + +// GTReference *head = [localRepo headReferenceWithError:&error]; +// expect(head).toNot(beNil()); + + }); +*/ + + /// Fast-Forward Merge + /// + /// Stages a pull by modifying a clone, resetting it back in history + /// then using pull to bring the repos back in sync. + it(@"fast-forwards one commit", ^{ + GTBranch *masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Reset local master back one commit + GTCommit *commit = [localRepo lookUpObjectByRevParse:@"HEAD^" error:&error]; + BOOL success = [localRepo resetToCommit:commit resetType:GTRepositoryResetTypeHard error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + // Verify rollback, must refresh branch from disk + masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@2)); + + // HEADs point to different objects + expect([[localRepo headReferenceWithError:NULL] OID]) + .toNot(equal([[remoteRepo headReferenceWithError:NULL] OID])); + + // Remote has 3 commits + GTBranch *remoteMasterBranch = localBranchWithName(@"master", remoteRepo); + expect(@([remoteMasterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Pull + __block BOOL transferProgressed = NO; + BOOL result = [localRepo pullBranch:masterBranch fromRemote:remote withOptions:nil error:&error progress:^(const git_transfer_progress *progress, BOOL *stop) { + transferProgressed = YES; + }]; + expect(error).to(beNil()); + expect(@(result)).to(beTruthy()); + expect(@(transferProgressed)).to(beFalsy()); // Local transport doesn't currently call progress callbacks + + // Verify same number of commits after pull, refresh branch from disk first + masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Verify HEADs are in sync + expect([[localRepo headReferenceWithError:NULL] OID]) + .to(equal([[remoteRepo headReferenceWithError:NULL] OID])); + }); + + /// Normal Merge + it(@"merges the upstream changes", ^{ + // 3 commits + GTBranch *masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@3)); + + // Create a new commit in the local repo + GTCommit *localCommit = createCommitInRepository(@"Local commit", [@"Test" dataUsingEncoding:NSUTF8StringEncoding], @"test.txt", localRepo); + expect(localCommit).notTo(beNil()); + + // 4 commits + masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@4)); + + localCommit = [localRepo lookUpObjectByOID:localCommit.OID objectType:GTObjectTypeCommit error:&error]; + expect(localCommit).notTo(beNil()); + expect(error).to(beNil()); + + // Create a new commit in the remote repo + GTCommit *upstreamCommit = createCommitInRepository(@"Upstream commit", [@"# So Fancy" dataUsingEncoding:NSUTF8StringEncoding], @"fancy.md", remoteRepo); + expect(upstreamCommit).notTo(beNil()); + + masterBranch = localBranchWithName(@"master", localRepo); + + // Validate there is one unique local commit before merge + BOOL success = NO; + GTBranch *remoteTrackingBranch = [masterBranch trackingBranchWithError:&error success:&success]; + expect(@(success)).to(beTrue()); + expect(error).to(beNil()); + expect(remoteTrackingBranch).toNot(beNil()); + + NSArray *uniqueLocalCommits = [localRepo localCommitsRelativeToRemoteBranch:remoteTrackingBranch error:&error]; + expect(uniqueLocalCommits).toNot(beNil()); + expect(error).to(beNil()); + expect(@(uniqueLocalCommits.count)).to(equal(@1)); + + // Pull + __block BOOL transferProgressed = NO; + BOOL result = [localRepo pullBranch:masterBranch fromRemote:remote withOptions:nil error:&error progress:^(const git_transfer_progress *progress, BOOL *stop) { + transferProgressed = YES; + }]; + expect(@(result)).to(beTruthy()); + expect(error).to(beNil()); + expect(@(transferProgressed)).to(beTruthy()); + + // Validate + + // 5 commits + masterBranch = localBranchWithName(@"master", localRepo); + expect(@([masterBranch numberOfCommitsWithError:NULL])).to(equal(@6)); + + // We should have have an additional merge commit after the pull + uniqueLocalCommits = [localRepo localCommitsRelativeToRemoteBranch:remoteTrackingBranch error:&error]; + expect(uniqueLocalCommits).toNot(beNil()); + expect(error).to(beNil()); + expect(@(uniqueLocalCommits.count)).to(equal(@3)); + }); + + /// Conflict During Merge + it(@"fails to merge when there is a conflict", ^{ + // Stage a conflict by adding the same file with different contents to both repos + GTCommit *localCommit = createCommitInRepository(@"Local commit", [@"TestLocal" dataUsingEncoding:NSUTF8StringEncoding], @"test.txt", localRepo); + expect(localCommit).notTo(beNil()); + GTCommit *remoteCommit = createCommitInRepository(@"Upstream commit", [@"TestUpstream" dataUsingEncoding:NSUTF8StringEncoding], @"test.txt", remoteRepo); + expect(remoteCommit).notTo(beNil()); + + GTBranch *masterBranch = localBranchWithName(@"master", localRepo); + + // Pull + __block BOOL transferProgressed = NO; + BOOL result = [localRepo pullBranch:masterBranch fromRemote:remote withOptions:nil error:&error progress:^(const git_transfer_progress *progress, BOOL *stop) { + transferProgressed = YES; + }]; + NSString *fileContents = [NSString stringWithContentsOfURL:[localRepo.fileURL URLByAppendingPathComponent:@"test.txt"] encoding:NSUTF8StringEncoding error:nil]; + expect(@(result)).to(beFalsy()); + expect(error.domain).to(equal(@"GTGitErrorDomain")); + expect(error.userInfo[GTPullMergeConflictedFiles]).to(equal(@[@"test.txt"])); + expect(fileContents).notTo(equal(@"TestLocal")); + expect([localRepo mergeHeadEntriesWithError:nil]).to(equal(@[remoteCommit.OID])); + expect([localRepo preparedMessageWithError:nil]).to(beginWith(@"Merge commit")); + expect(error.localizedDescription).to(equal(@"Merge conflict")); + expect(@(transferProgressed)).to(beTruthy()); + }); + + }); + +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRepository+StatusSpec.m b/ObjectiveGitTests/GTRepository+StatusSpec.m index 432ce550a..c2ceaa9cb 100644 --- a/ObjectiveGitTests/GTRepository+StatusSpec.m +++ b/ObjectiveGitTests/GTRepository+StatusSpec.m @@ -6,7 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -SpecBegin(GTRepositoryStatus) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTRepositoryStatus) describe(@"Checking status", ^{ __block GTRepository *repository = nil; @@ -14,91 +20,113 @@ __block NSError *err; NSData *testData = [@"test" dataUsingEncoding:NSUTF8StringEncoding]; - + beforeEach(^{ repository = self.testAppFixtureRepository; targetFileURL = [repository.fileURL URLByAppendingPathComponent:@"main.m"]; - expect(repository).toNot.beNil(); + expect(repository).notTo(beNil()); }); - - void (^updateIndexForSubpathAndExpectStatus)(NSString *, GTStatusDeltaStatus) = ^(NSString *subpath, GTStatusDeltaStatus expectedIndexStatus) { + + void (^updateIndexForSubpathAndExpectStatus)(NSString *, GTDeltaType) = ^(NSString *subpath, GTDeltaType expectedIndexStatus) { __block NSError *err = nil; GTIndex *index = [repository indexWithError:&err]; - expect(err).to.beNil(); - expect(index).toNot.beNil(); - expect([index updatePathspecs:NULL error:NULL passingTest:NULL]).to.beTruthy(); + expect(err).to(beNil()); + expect(index).notTo(beNil()); + expect(@([index updatePathspecs:NULL error:NULL passingTest:NULL])).to(beTruthy()); NSDictionary *renamedOptions = @{ GTRepositoryStatusOptionsFlagsKey: @(GTRepositoryStatusFlagsIncludeIgnored | GTRepositoryStatusFlagsIncludeUntracked | GTRepositoryStatusFlagsRecurseUntrackedDirectories | GTRepositoryStatusFlagsRenamesHeadToIndex) }; - expect([repository enumerateFileStatusWithOptions:renamedOptions error:&err usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { + expect(@([repository enumerateFileStatusWithOptions:renamedOptions error:&err usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { if (![headToIndex.newFile.path isEqualToString:subpath]) return; - expect(headToIndex.status).to.equal(expectedIndexStatus); - }]).to.beTruthy(); - expect(err).to.beNil(); + expect(@(headToIndex.status)).to(equal(@(expectedIndexStatus))); + }])).to(beTruthy()); + expect(err).to(beNil()); }; - - void (^expectSubpathToHaveWorkDirStatus)(NSString *, GTStatusDeltaStatus) = ^(NSString *subpath, GTStatusDeltaStatus expectedWorkDirStatus) { + + void (^expectSubpathToHaveWorkDirStatus)(NSString *, GTDeltaType) = ^(NSString *subpath, GTDeltaType expectedWorkDirStatus) { __block NSError *err = nil; NSDictionary *renamedOptions = @{ GTRepositoryStatusOptionsFlagsKey: @(GTRepositoryStatusFlagsIncludeIgnored | GTRepositoryStatusFlagsIncludeUntracked | GTRepositoryStatusFlagsRecurseUntrackedDirectories | GTRepositoryStatusFlagsRenamesIndexToWorkingDirectory) }; - expect([repository enumerateFileStatusWithOptions:renamedOptions error:&err usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { + expect(@([repository enumerateFileStatusWithOptions:renamedOptions error:&err usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { if (![indexToWorkingDirectory.newFile.path isEqualToString:subpath]) return; - expect(indexToWorkingDirectory.status).to.equal(expectedWorkDirStatus); - }]).to.beTruthy(); - expect(err).to.beNil(); + expect(@(indexToWorkingDirectory.status)).to(equal(@(expectedWorkDirStatus))); + }])).to(beTruthy()); + expect(err).to(beNil()); }; - - void (^expectSubpathToHaveMatchingStatus)(NSString *, GTStatusDeltaStatus) = ^(NSString *subpath, GTStatusDeltaStatus status) { + + void (^expectSubpathToHaveMatchingStatus)(NSString *, GTDeltaType) = ^(NSString *subpath, GTDeltaType status) { expectSubpathToHaveWorkDirStatus(subpath, status); updateIndexForSubpathAndExpectStatus(subpath, status); }; - + it(@"should recognize untracked files", ^{ - expectSubpathToHaveWorkDirStatus(@"UntrackedImage.png", GTStatusDeltaStatusUntracked); + expectSubpathToHaveWorkDirStatus(@"UntrackedImage.png", GTDeltaTypeUntracked); }); - + it(@"should recognize added files", ^{ - updateIndexForSubpathAndExpectStatus(@"UntrackedImage.png", GTStatusDeltaStatusAdded); + updateIndexForSubpathAndExpectStatus(@"UntrackedImage.png", GTDeltaTypeAdded); }); - + it(@"should recognize modified files", ^{ - expect([NSFileManager.defaultManager removeItemAtURL:targetFileURL error:&err]).to.beTruthy(); - expect(err).to.beNil(); - expect([testData writeToURL:targetFileURL atomically:YES]).to.beTruthy(); - expectSubpathToHaveMatchingStatus(targetFileURL.lastPathComponent, GTStatusDeltaStatusModified); + expect(@([NSFileManager.defaultManager removeItemAtURL:targetFileURL error:&err])).to(beTruthy()); + expect(err).to(beNil()); + expect(@([testData writeToURL:targetFileURL atomically:YES])).to(beTruthy()); + expectSubpathToHaveMatchingStatus(targetFileURL.lastPathComponent, GTDeltaTypeModified); }); - + it(@"should recognize copied files", ^{ NSURL *copyLocation = [repository.fileURL URLByAppendingPathComponent:@"main2.m"]; - expect([NSFileManager.defaultManager copyItemAtURL:targetFileURL toURL:copyLocation error:&err]).to.beTruthy(); - expect(err).to.beNil(); - updateIndexForSubpathAndExpectStatus(copyLocation.lastPathComponent, GTStatusDeltaStatusCopied); + expect(@([NSFileManager.defaultManager copyItemAtURL:targetFileURL toURL:copyLocation error:&err])).to(beTruthy()); + expect(err).to(beNil()); + updateIndexForSubpathAndExpectStatus(copyLocation.lastPathComponent, GTDeltaTypeCopied); }); - + it(@"should recognize deleted files", ^{ - expect([NSFileManager.defaultManager removeItemAtURL:targetFileURL error:&err]).to.beTruthy(); - expect(err).to.beNil(); - expectSubpathToHaveMatchingStatus(targetFileURL.lastPathComponent, GTStatusDeltaStatusDeleted); + expect(@([NSFileManager.defaultManager removeItemAtURL:targetFileURL error:&err])).to(beTruthy()); + expect(err).to(beNil()); + expectSubpathToHaveMatchingStatus(targetFileURL.lastPathComponent, GTDeltaTypeDeleted); }); - + it(@"should recognize renamed files", ^{ NSURL *moveLocation = [repository.fileURL URLByAppendingPathComponent:@"main-moved.m"]; - expect([NSFileManager.defaultManager moveItemAtURL:targetFileURL toURL:moveLocation error:&err]).to.beTruthy(); - expect(err).to.beNil(); - expectSubpathToHaveWorkDirStatus(moveLocation.lastPathComponent, GTStatusDeltaStatusRenamed); + expect(@([NSFileManager.defaultManager moveItemAtURL:targetFileURL toURL:moveLocation error:&err])).to(beTruthy()); + expect(err).to(beNil()); + expectSubpathToHaveWorkDirStatus(moveLocation.lastPathComponent, GTDeltaTypeRenamed); }); - + it(@"should recognise ignored files", ^{ //at least in the default options - expectSubpathToHaveWorkDirStatus(@".DS_Store", GTStatusDeltaStatusIgnored); + expectSubpathToHaveWorkDirStatus(@".DS_Store", GTDeltaTypeIgnored); }); - + it(@"should skip ignored files if asked", ^{ __block NSError *err = nil; NSDictionary *options = @{ GTRepositoryStatusOptionsFlagsKey: @(0) }; BOOL enumerationSuccessful = [repository enumerateFileStatusWithOptions:options error:&err usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { - expect(indexToWorkingDirectory.status).toNot.equal(GTStatusDeltaStatusIgnored); + expect(@(indexToWorkingDirectory.status)).notTo(equal(@(GTDeltaTypeIgnored))); }]; - expect(enumerationSuccessful).to.beTruthy(); - expect(err).to.beNil(); + expect(@(enumerationSuccessful)).to(beTruthy()); + expect(err).to(beNil()); }); + + it(@"should report file should be ignored", ^{ + __block NSError *err = nil; + NSURL *fileURL = [repository.fileURL URLByAppendingPathComponent:@".DS_Store"]; + BOOL success = NO; + BOOL shouldIgnore = [repository shouldFileBeIgnored:fileURL success:&success error:&err]; + expect(@(success)).to(beTrue()); + expect(@(shouldIgnore)).to(beTrue()); + expect(err).to(beNil()); + }); + + it(@"should report file should be ignored (convenience wrapper)", ^{ + __block NSError *err = nil; + NSURL *fileURL = [repository.fileURL URLByAppendingPathComponent:@".DS_Store"]; + GTFileIgnoreState ignore = [repository shouldIgnoreFileURL:fileURL error:&err]; + expect(@(ignore)).to(equal(@(GTFileIgnoreStateShouldIgnore))); + expect(err).to(beNil()); + }); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRepositoryAttributesSpec.m b/ObjectiveGitTests/GTRepositoryAttributesSpec.m new file mode 100644 index 000000000..e60737eb0 --- /dev/null +++ b/ObjectiveGitTests/GTRepositoryAttributesSpec.m @@ -0,0 +1,39 @@ +// +// GTRepositoryAttributesSpec.m +// ObjectiveGitFramework +// +// Created by Josh Abernathy on 7/25/14. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTRepositoryAttributes) + +__block GTRepository *repository; + +beforeEach(^{ + repository = [self blankFixtureRepository]; +}); + +it(@"should be able to look up attributes", ^{ + static NSString * const testAttributes = @"*.txt filter=reverse"; + NSURL *attributesURL = [repository.fileURL URLByAppendingPathComponent:@".gitattributes"]; + BOOL success = [testAttributes writeToURL:attributesURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]; + expect(@(success)).to(beTruthy()); + + NSString *value = [repository attributeWithName:@"filter" path:@"*.txt"]; + expect(value).to(equal(@"reverse")); + + value = [repository attributeWithName:@"filter" path:@"thing.txt"]; + expect(value).to(equal(@"reverse")); + + value = [repository attributeWithName:@"filter" path:@"thing.jpg"]; + expect(value).to(beNil()); +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRepositoryCommittingSpec.m b/ObjectiveGitTests/GTRepositoryCommittingSpec.m index 1708eaa29..8e0ffa3f8 100644 --- a/ObjectiveGitTests/GTRepositoryCommittingSpec.m +++ b/ObjectiveGitTests/GTRepositoryCommittingSpec.m @@ -6,10 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTRepository.h" -#import "GTRepository+Committing.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTRepositoryCommitting) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTRepositoryCommitting) __block GTRepository *repository; @@ -19,37 +22,41 @@ CFRelease(UUIDRef); NSURL *fileURL = [self.tempDirectoryFileURL URLByAppendingPathComponent:UUID isDirectory:NO]; - repository = [GTRepository initializeEmptyRepositoryAtFileURL:fileURL error:NULL]; - expect(repository).notTo.beNil(); + repository = [GTRepository initializeEmptyRepositoryAtFileURL:fileURL options:nil error:NULL]; + expect(repository).notTo(beNil()); }); it(@"can create commits", ^{ - GTTreeBuilder *builder = [[GTTreeBuilder alloc] initWithTree:nil error:NULL]; - expect(builder).toNot.beNil(); + GTTreeBuilder *builder = [[GTTreeBuilder alloc] initWithTree:nil repository:repository error:NULL]; + expect(builder).notTo(beNil()); GTTreeEntry *entry = [builder addEntryWithData:[@"Another file contents" dataUsingEncoding:NSUTF8StringEncoding] fileName:@"Test file 2.txt" fileMode:GTFileModeBlob error:NULL]; - expect(entry).notTo.beNil(); + expect(entry).notTo(beNil()); - GTTree *subtree = [builder writeTreeToRepository:repository error:NULL]; - expect(subtree).notTo.beNil(); + GTTree *subtree = [builder writeTree:NULL]; + expect(subtree).notTo(beNil()); [builder clear]; entry = [builder addEntryWithData:[@"Test contents" dataUsingEncoding:NSUTF8StringEncoding] fileName:@"Test file.txt" fileMode:GTFileModeBlob error:NULL]; - expect(entry).notTo.beNil(); + expect(entry).notTo(beNil()); entry = [builder addEntryWithOID:subtree.OID fileName:@"subdir" fileMode:GTFileModeTree error:NULL]; - expect(entry).notTo.beNil(); + expect(entry).notTo(beNil()); - GTTree *tree = [builder writeTreeToRepository:repository error:NULL]; - expect(tree).notTo.beNil(); + GTTree *tree = [builder writeTree:NULL]; + expect(tree).notTo(beNil()); GTCommit *initialCommit = [repository createCommitWithTree:tree message:@"Initial commit" parents:nil updatingReferenceNamed:@"refs/heads/master" error:NULL]; - expect(initialCommit).notTo.beNil(); + expect(initialCommit).notTo(beNil()); GTReference *ref = [repository headReferenceWithError:NULL]; - expect(ref).notTo.beNil(); - expect(ref.resolvedTarget).to.equal(initialCommit); + expect(ref).notTo(beNil()); + expect(ref.resolvedTarget).to(equal(initialCommit)); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRepositoryResetSpec.m b/ObjectiveGitTests/GTRepositoryResetSpec.m index c7cab9822..eab048715 100644 --- a/ObjectiveGitTests/GTRepositoryResetSpec.m +++ b/ObjectiveGitTests/GTRepositoryResetSpec.m @@ -6,11 +6,13 @@ // Copyright (c) 2014 GitHub, Inc. All rights reserved. // -#import "GTRepository+Reset.h" -#import "GTRepository.h" -#import "GTIndex.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTRepositoryReset) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTRepositoryReset) __block GTRepository *repository; @@ -23,7 +25,7 @@ countStagedFiles = ^{ __block NSUInteger count = 0; [repository enumerateFileStatusWithOptions:nil error:NULL usingBlock:^(GTStatusDelta *headToIndex, GTStatusDelta *indexToWorkingDirectory, BOOL *stop) { - if (headToIndex.status != GTStatusDeltaStatusUnmodified) count++; + if (headToIndex.status != GTDeltaTypeUnmodified) count++; }]; return count; @@ -34,23 +36,23 @@ static NSString * const fileName = @"README.md"; NSURL *fileURL = [repository.fileURL URLByAppendingPathComponent:fileName]; BOOL success = [@"blahahaha" writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); GTIndex *index = [repository indexWithError:NULL]; - expect(index).notTo.beNil(); + expect(index).notTo(beNil()); success = [index addFile:fileName error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); - expect(countStagedFiles()).to.equal(1); + expect(@(countStagedFiles())).to(equal(@1)); GTCommit *HEAD = [repository lookUpObjectByRevParse:@"HEAD" error:NULL]; - expect(HEAD).notTo.beNil(); + expect(HEAD).notTo(beNil()); success = [repository resetPathspecs:@[ fileName ] toCommit:HEAD error:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); - expect(countStagedFiles()).to.equal(0); + expect(@(countStagedFiles())).to(equal(@0)); }); }); @@ -65,25 +67,29 @@ NSString *resetTargetSHA = @"8496071c1b46c854b31185ea97743be6a8774479"; GTCommit *commit = [repository lookUpObjectBySHA:resetTargetSHA error:NULL]; - expect(commit).notTo.beNil(); - GTCommit *originalHeadCommit = [repository lookUpObjectBySHA:originalHead.targetSHA error:NULL]; - expect(originalHeadCommit).notTo.beNil(); + expect(commit).notTo(beNil()); + GTCommit *originalHeadCommit = [repository lookUpObjectByOID:originalHead.targetOID error:NULL]; + expect(originalHeadCommit).notTo(beNil()); BOOL success = [repository resetToCommit:commit resetType:GTRepositoryResetTypeSoft error:&error]; - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); GTReference *head = [repository headReferenceWithError:&error]; - expect(head).notTo.beNil(); - expect(head.targetSHA).to.equal(resetTargetSHA); + expect(head).notTo(beNil()); + expect(head.targetOID.SHA).to(equal(resetTargetSHA)); success = [repository resetToCommit:originalHeadCommit resetType:GTRepositoryResetTypeSoft error:&error]; - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); head = [repository headReferenceWithError:&error]; - expect(head.targetSHA).to.equal(originalHead.targetSHA); + expect(head.targetOID).to(equal(originalHead.targetOID)); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRepositorySpec.m b/ObjectiveGitTests/GTRepositorySpec.m index 60f8c447b..2ff1aaf88 100644 --- a/ObjectiveGitTests/GTRepositorySpec.m +++ b/ObjectiveGitTests/GTRepositorySpec.m @@ -6,36 +6,51 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTRepository.h" -#import "GTRepository+Committing.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTRepositorySpec) + +static NSString * const readmeFile = @"README.md"; +static NSString * const readme1File = @"README1.txt"; -SpecBegin(GTRepository) __block GTRepository *repository; beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); }); describe(@"+initializeEmptyRepositoryAtFileURL:bare:error:", ^{ it(@"should initialize a repository with a working directory by default", ^{ NSURL *newRepoURL = [self.tempDirectoryFileURL URLByAppendingPathComponent:@"init-repo"]; - GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:newRepoURL bare:NO error:NULL]; - expect(repository).notTo.beNil(); - expect(repository.gitDirectoryURL).notTo.beNil(); - expect(repository.bare).to.beFalsy(); + NSError *error; + GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:newRepoURL options:nil error:&error]; + expect(repository).notTo(beNil()); + expect(error).to(beNil()); + + expect(repository.gitDirectoryURL).notTo(beNil()); + expect(@(repository.bare)).to(beFalsy()); }); it(@"should initialize a bare repository", ^{ NSURL *newRepoURL = [self.tempDirectoryFileURL URLByAppendingPathComponent:@"init-repo.git"]; - - GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:newRepoURL bare:YES error:NULL]; - expect(repository).notTo.beNil(); - expect(repository.gitDirectoryURL).notTo.beNil(); - return repository; - expect(repository.bare).to.beTruthy(); + NSDictionary *options = @{ + GTRepositoryInitOptionsFlags: @(GTRepositoryInitBare | GTRepositoryInitCreatingRepositoryDirectory) + }; + + NSError *error; + GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:newRepoURL options:options error:&error]; + expect(repository).notTo(beNil()); + expect(error).to(beNil()); + + expect(repository.gitDirectoryURL).notTo(beNil()); + expect(@(repository.bare)).to(beTruthy()); }); }); @@ -43,17 +58,17 @@ it(@"should fail to initialize non-existent repos", ^{ NSError *error = nil; GTRepository *badRepo = [GTRepository repositoryWithURL:[NSURL fileURLWithPath:@"fake/1235"] error:&error]; - expect(badRepo).to.beNil(); - expect(error).notTo.beNil(); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_ENOTFOUND); + expect(badRepo).to(beNil()); + expect(error).notTo(beNil()); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_ENOTFOUND))); }); }); describe(@"+cloneFromURL:toWorkingDirectory:options:error:transferProgressBlock:checkoutProgressBlock:", ^{ __block BOOL transferProgressCalled = NO; __block BOOL checkoutProgressCalled = NO; - __block void (^transferProgressBlock)(const git_transfer_progress *); + __block void (^transferProgressBlock)(const git_transfer_progress *, BOOL *); __block void (^checkoutProgressBlock)(NSString *, NSUInteger, NSUInteger); __block NSURL *originURL; __block NSURL *workdirURL; @@ -63,7 +78,7 @@ beforeEach(^{ transferProgressCalled = NO; checkoutProgressCalled = NO; - transferProgressBlock = ^(const git_transfer_progress *progress) { + transferProgressBlock = ^(const git_transfer_progress *progress, BOOL *stop) { transferProgressCalled = YES; }; checkoutProgressBlock = ^(NSString *path, NSUInteger completedSteps, NSUInteger totalSteps) { @@ -80,88 +95,150 @@ it(@"should handle normal clones", ^{ NSError *error = nil; - repository = [GTRepository cloneFromURL:originURL toWorkingDirectory:workdirURL options:nil error:&error transferProgressBlock:transferProgressBlock checkoutProgressBlock:checkoutProgressBlock]; - expect(repository).notTo.beNil(); - expect(error).to.beNil(); - expect(transferProgressCalled).to.beTruthy(); - expect(checkoutProgressCalled).to.beTruthy(); + GTCheckoutOptions *checkoutOptions = [GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategySafe]; + checkoutOptions.progressBlock = checkoutProgressBlock; - expect(repository.isBare).to.beFalsy(); + NSDictionary *cloneOptions = @{ + GTRepositoryCloneOptionsCloneLocal: @YES, + GTRepositoryCloneOptionsCheckoutOptions: checkoutOptions, + }; + repository = [GTRepository cloneFromURL:originURL toWorkingDirectory:workdirURL options:cloneOptions error:&error transferProgressBlock:transferProgressBlock]; + expect(repository).notTo(beNil()); + expect(error).to(beNil()); + expect(@(transferProgressCalled)).to(beTruthy()); + expect(@(checkoutProgressCalled)).to(beTruthy()); + + expect(@(repository.isBare)).to(beFalsy()); GTReference *head = [repository headReferenceWithError:&error]; - expect(head).notTo.beNil(); - expect(error).to.beNil(); - expect(head.targetSHA).to.equal(@"36060c58702ed4c2a40832c51758d5344201d89a"); - expect(head.referenceType).to.equal(GTReferenceTypeOid); + expect(head).notTo(beNil()); + expect(error).to(beNil()); + expect(head.targetOID.SHA).to(equal(@"36060c58702ed4c2a40832c51758d5344201d89a")); + expect(@(head.referenceType)).to(equal(@(GTReferenceTypeDirect))); }); it(@"should handle bare clones", ^{ NSError *error = nil; - NSDictionary *options = @{ GTRepositoryCloneOptionsBare: @YES }; - repository = [GTRepository cloneFromURL:originURL toWorkingDirectory:workdirURL options:options error:&error transferProgressBlock:transferProgressBlock checkoutProgressBlock:checkoutProgressBlock]; - expect(repository).notTo.beNil(); - expect(error).to.beNil(); - expect(transferProgressCalled).to.beTruthy(); - expect(checkoutProgressCalled).to.beFalsy(); - - expect(repository.isBare).to.beTruthy(); + GTCheckoutOptions *checkoutOptions = [GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategySafe]; + checkoutOptions.progressBlock = checkoutProgressBlock; + + NSDictionary *options = @{ + GTRepositoryCloneOptionsBare: @YES, + GTRepositoryCloneOptionsCloneLocal: @YES, + GTRepositoryCloneOptionsCheckoutOptions: checkoutOptions, + }; + repository = [GTRepository cloneFromURL:originURL toWorkingDirectory:workdirURL options:options error:&error transferProgressBlock:transferProgressBlock]; + expect(repository).notTo(beNil()); + expect(error).to(beNil()); + expect(@(transferProgressCalled)).to(beTruthy()); + expect(@(checkoutProgressCalled)).to(beFalsy()); + + expect(@(repository.isBare)).to(beTruthy()); GTReference *head = [repository headReferenceWithError:&error]; - expect(head).notTo.beNil(); - expect(error).to.beNil(); - expect(head.targetSHA).to.equal(@"36060c58702ed4c2a40832c51758d5344201d89a"); - expect(head.referenceType).to.equal(GTReferenceTypeOid); + expect(head).notTo(beNil()); + expect(error).to(beNil()); + expect(head.targetOID.SHA).to(equal(@"36060c58702ed4c2a40832c51758d5344201d89a")); + expect(@(head.referenceType)).to(equal(@(GTReferenceTypeDirect))); }); it(@"should have set a valid remote URL", ^{ NSError *error = nil; - GTRepository *repo = [GTRepository cloneFromURL:originURL toWorkingDirectory:workdirURL options:nil error:&error transferProgressBlock:transferProgressBlock checkoutProgressBlock:checkoutProgressBlock]; - expect(repo).notTo.beNil(); - expect(error).to.beNil(); - - // FIXME: Move that to a method in GTRepository ? - // Or use the new initializers in GTRemote that are waiting in #224 - git_remote *remote; - git_remote_load(&remote, repo.git_repository, "origin"); - GTRemote *originRemote = [[GTRemote alloc] initWithGitRemote:remote]; - expect(originRemote.URLString).to.equal(originURL.path); + GTCheckoutOptions *checkoutOptions = [GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategySafe]; + checkoutOptions.progressBlock = checkoutProgressBlock; + + repository = [GTRepository cloneFromURL:originURL toWorkingDirectory:workdirURL options:@{ GTRepositoryCloneOptionsCheckoutOptions: checkoutOptions } error:&error transferProgressBlock:transferProgressBlock]; + expect(repository).notTo(beNil()); + expect(error).to(beNil()); + + GTRemote *originRemote = [GTRemote remoteWithName:@"origin" inRepository:repository error:&error]; + expect(error).to(beNil()); + expect(originRemote.URLString).to(equal(originURL.path)); }); }); + + describe(@"with remote repositories", ^{ + __block GTCredentialProvider *provider = nil; + NSString *userName = [[NSProcessInfo processInfo] environment][@"GTUserName"]; + NSString *publicKeyPath = [[[NSProcessInfo processInfo] environment][@"GTPublicKey"] stringByStandardizingPath]; + NSString *privateKeyPath = [[[NSProcessInfo processInfo] environment][@"GTPrivateKey"] stringByStandardizingPath]; + NSString *privateKeyPassword = [[NSProcessInfo processInfo] environment][@"GTPrivateKeyPassword"]; + + beforeEach(^{ + // Let's clone libgit2's documentation + originURL = [NSURL URLWithString:@"git@github.com:libgit2/libgit2.github.amrom.workers.dev.git"]; + }); + + if (userName && publicKeyPath && privateKeyPath && privateKeyPassword) { + it(@"should handle clones", ^{ + __block NSError *error = nil; + + provider = [GTCredentialProvider providerWithBlock:^GTCredential *(GTCredentialType type, NSString *URL, NSString *credUserName) { + expect(URL).to(equal(originURL.absoluteString)); + expect(@(type & GTCredentialTypeSSHKey)).to(beTruthy()); + GTCredential *cred = nil; + // cred = [GTCredential credentialWithUserName:userName password:password error:&error]; + cred = [GTCredential credentialWithUserName:credUserName publicKeyURL:[NSURL fileURLWithPath:publicKeyPath] privateKeyURL:[NSURL fileURLWithPath:privateKeyPath] passphrase:privateKeyPassword error:&error]; + expect(cred).notTo(beNil()); + expect(error).to(beNil()); + return cred; + }]; + + GTCheckoutOptions *checkoutOptions = [GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategySafe]; + checkoutOptions.progressBlock = checkoutProgressBlock; + NSDictionary *cloneOptions = @{ + GTRepositoryCloneOptionsCredentialProvider: provider, + GTRepositoryCloneOptionsCheckoutOptions: checkoutOptions, + }; + + repository = [GTRepository cloneFromURL:originURL toWorkingDirectory:workdirURL options:cloneOptions error:&error transferProgressBlock:transferProgressBlock]; + expect(repository).notTo(beNil()); + expect(error).to(beNil()); + expect(@(transferProgressCalled)).to(beTruthy()); + expect(@(checkoutProgressCalled)).to(beTruthy()); + + GTRemote *originRemote = [GTRemote remoteWithName:@"origin" inRepository:repository error:&error]; + expect(error).to(beNil()); + expect(originRemote.URLString).to(equal(originURL.absoluteString)); + }); + } + }); }); describe(@"-headReferenceWithError:", ^{ it(@"should allow HEAD to be looked up", ^{ NSError *error = nil; GTReference *head = [self.bareFixtureRepository headReferenceWithError:&error]; - expect(head).notTo.beNil(); - expect(error).to.beNil(); - expect(head.targetSHA).to.equal(@"36060c58702ed4c2a40832c51758d5344201d89a"); - expect(head.referenceType).to.equal(GTReferenceTypeOid); + expect(head).notTo(beNil()); + expect(error).to(beNil()); + expect(head.targetOID.SHA).to(equal(@"36060c58702ed4c2a40832c51758d5344201d89a")); + expect(@(head.referenceType)).to(equal(@(GTReferenceTypeDirect))); }); it(@"should fail to return HEAD for an unborn repo", ^{ GTRepository *repo = self.blankFixtureRepository; - expect(repo.isHEADUnborn).to.beTruthy(); + expect(@(repo.isHEADUnborn)).to(beTruthy()); NSError *error = nil; GTReference *head = [repo headReferenceWithError:&error]; - expect(head).to.beNil(); - expect(error).notTo.beNil(); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_EUNBORNBRANCH); + expect(head).to(beNil()); + expect(error).notTo(beNil()); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_EUNBORNBRANCH))); }); }); describe(@"-isEmpty", ^{ it(@"should return NO for a non-empty repository", ^{ - expect(repository.isEmpty).to.beFalsy(); + expect(@(repository.isEmpty)).to(beFalsy()); }); it(@"should return YES for a new repository", ^{ NSError *error = nil; NSURL *fileURL = [self.tempDirectoryFileURL URLByAppendingPathComponent:@"newrepo"]; - GTRepository *newRepo = [GTRepository initializeEmptyRepositoryAtFileURL:fileURL error:&error]; - expect(newRepo.isEmpty).to.beTruthy(); + GTRepository *newRepo = [GTRepository initializeEmptyRepositoryAtFileURL:fileURL options:nil error:&error]; + expect(newRepo).notTo(beNil()); + expect(@(newRepo.isEmpty)).to(beTruthy()); [NSFileManager.defaultManager removeItemAtURL:fileURL error:NULL]; }); }); @@ -169,17 +246,59 @@ describe(@"-preparedMessage", ^{ it(@"should return nil by default", ^{ __block NSError *error = nil; - expect([repository preparedMessageWithError:&error]).to.beNil(); - expect(error).to.beNil(); + expect([repository preparedMessageWithError:&error]).to(beNil()); + expect(error).to(beNil()); }); it(@"should return the contents of MERGE_MSG", ^{ NSString *message = @"Commit summary\n\ndescription"; - expect([message writeToURL:[repository.gitDirectoryURL URLByAppendingPathComponent:@"MERGE_MSG"] atomically:YES encoding:NSUTF8StringEncoding error:NULL]).to.beTruthy(); + expect(@([message writeToURL:[repository.gitDirectoryURL URLByAppendingPathComponent:@"MERGE_MSG"] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); __block NSError *error = nil; - expect([repository preparedMessageWithError:&error]).to.equal(message); - expect(error).to.beNil(); + expect([repository preparedMessageWithError:&error]).to(equal(message)); + expect(error).to(beNil()); + }); +}); + +describe(@"-contentsOfDiffWithAncestor:ourSide:theirSide:error:", ^{ + it(@"should produce a nice merge conflict description", ^{ + NSURL *mainURL = [repository.fileURL URLByAppendingPathComponent:@"main.m"]; + NSData *mainData = [[NSFileManager defaultManager] contentsAtPath:mainURL.path]; + expect(mainData).notTo(beNil()); + + NSString *mainString = [[NSString alloc] initWithData:mainData encoding:NSUTF8StringEncoding]; + NSData *masterData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 41\n return"] dataUsingEncoding:NSUTF8StringEncoding]; + NSData *otherData = [[mainString stringByReplacingOccurrencesOfString:@"return" withString:@"//The meaning of life is 42\n return"] dataUsingEncoding:NSUTF8StringEncoding]; + + expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:masterData attributes:nil])).to(beTruthy()); + + GTIndex *index = [repository indexWithError:NULL]; + expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy()); + GTReference *head = [repository headReferenceWithError:NULL]; + GTCommit *parent = [repository lookUpObjectByOID:head.targetOID objectType:GTObjectTypeCommit error:NULL]; + expect(parent).toNot(beNil()); + GTTree *masterTree = [index writeTree:NULL]; + expect(masterTree).toNot(beNil()); + + GTBranch *otherBranch = [repository lookUpBranchWithName:@"other-branch" type:GTBranchTypeLocal success:NULL error:NULL]; + expect(otherBranch).toNot(beNil()); + expect(@([repository checkoutReference:otherBranch.reference options:nil error:NULL])).to(beTruthy()); + + expect(@([[NSFileManager defaultManager] createFileAtPath:mainURL.path contents:otherData attributes:nil])).to(beTruthy()); + + index = [repository indexWithError:NULL]; + expect(@([index addFile:mainURL.lastPathComponent error:NULL])).to(beTruthy()); + GTTree *otherTree = [index writeTree:NULL]; + expect(otherTree).toNot(beNil()); + + GTIndex *conflictIndex = [otherTree merge:masterTree ancestor:parent.tree error:NULL]; + expect(@([conflictIndex hasConflicts])).to(beTruthy()); + + [conflictIndex enumerateConflictedFilesWithError:NULL usingBlock:^(GTIndexEntry * _Nonnull ancestor, GTIndexEntry * _Nonnull ours, GTIndexEntry * _Nonnull theirs, BOOL * _Nonnull stop) { + + NSString *conflictString = [repository contentsOfDiffWithAncestor:ancestor ourSide:ours theirSide:theirs error:NULL]; + expect(conflictString).to(equal(@"//\n// main.m\n// Test\n//\n// Created by Joe Ricioppo on 9/28/10.\n// Copyright 2010 __MyCompanyName__. All rights reserved.\n//\n\n#import \n\nint main(int argc, char *argv[])\n{\n<<<<<<< file.txt\n //The meaning of life is 42\n=======\n //The meaning of life is 41\n>>>>>>> file.txt\n return NSApplicationMain(argc, (const char **) argv);\n}\n123456789\n123456789\n123456789\n123456789!blah!\n")); + }]; }); }); @@ -187,16 +306,16 @@ it(@"should find the merge base between two branches", ^{ NSError *error = nil; GTBranch *masterBranch = [repository lookUpBranchWithName:@"master" type:GTBranchTypeLocal success:NULL error:&error]; - expect(masterBranch).notTo.beNil(); - expect(error).to.beNil(); + expect(masterBranch).notTo(beNil()); + expect(error).to(beNil()); GTBranch *otherBranch = [repository lookUpBranchWithName:@"other-branch" type:GTBranchTypeLocal success:NULL error:&error]; - expect(otherBranch).notTo.beNil(); - expect(error).to.beNil(); + expect(otherBranch).notTo(beNil()); + expect(error).to(beNil()); GTCommit *mergeBase = [repository mergeBaseBetweenFirstOID:masterBranch.reference.OID secondOID:otherBranch.reference.OID error:&error]; - expect(mergeBase).notTo.beNil(); - expect(mergeBase.SHA).to.equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"); + expect(mergeBase).notTo(beNil()); + expect(mergeBase.SHA).to(equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0")); }); }); @@ -204,8 +323,8 @@ it(@"should return all tags", ^{ NSError *error = nil; NSArray *tags = [repository allTagsWithError:&error]; - expect(tags).notTo.beNil(); - expect(tags.count).to.equal(0); + expect(tags).notTo(beNil()); + expect(@(tags.count)).to(equal(@0)); }); }); @@ -213,27 +332,27 @@ it(@"should return the current branch", ^{ NSError *error = nil; GTBranch *currentBranch = [repository currentBranchWithError:&error]; - expect(currentBranch).notTo.beNil(); - expect(error).to.beNil(); - expect(currentBranch.name).to.equal(@"refs/heads/master"); + expect(currentBranch).notTo(beNil()); + expect(error).to(beNil()); + expect(currentBranch.name).to(equal(@"master")); }); }); describe(@"-createBranchNamed:fromOID:committer:message:error:", ^{ it(@"should create a local branch from the given OID", ^{ GTBranch *currentBranch = [repository currentBranchWithError:NULL]; - expect(currentBranch).notTo.beNil(); + expect(currentBranch).notTo(beNil()); NSString *branchName = @"new-test-branch"; NSError *error = nil; - GTBranch *newBranch = [repository createBranchNamed:branchName fromOID:[[GTOID alloc] initWithSHA:currentBranch.SHA] committer:nil message:nil error:&error]; - expect(newBranch).notTo.beNil(); - expect(error).to.beNil(); + GTBranch *newBranch = [repository createBranchNamed:branchName fromOID:currentBranch.OID message:nil error:&error]; + expect(newBranch).notTo(beNil()); + expect(error).to(beNil()); - expect(newBranch.shortName).to.equal(branchName); - expect(newBranch.branchType).to.equal(GTBranchTypeLocal); - expect(newBranch.SHA).to.equal(currentBranch.SHA); + expect(newBranch.shortName).to(equal(branchName)); + expect(@(newBranch.branchType)).to(equal(@(GTBranchTypeLocal))); + expect(newBranch.OID).to(equal(currentBranch.OID)); }); }); @@ -241,9 +360,9 @@ it(@"should return the local branches", ^{ NSError *error = nil; NSArray *branches = [repository localBranchesWithError:&error]; - expect(branches).notTo.beNil(); - expect(error).to.beNil(); - expect(branches.count).to.equal(13); + expect(branches).notTo(beNil()); + expect(error).to(beNil()); + expect(@(branches.count)).to(equal(@13)); }); }); @@ -251,11 +370,11 @@ it(@"should return remote branches", ^{ NSError *error = nil; NSArray *branches = [repository remoteBranchesWithError:&error]; - expect(branches).notTo.beNil(); - expect(error).to.beNil(); - expect(branches.count).to.equal(1); + expect(branches).notTo(beNil()); + expect(error).to(beNil()); + expect(@(branches.count)).to(equal(@1)); GTBranch *remoteBranch = branches[0]; - expect(remoteBranch.name).to.equal(@"refs/remotes/origin/master"); + expect(remoteBranch.name).to(equal(@"origin/master")); }); }); @@ -263,12 +382,12 @@ it(@"should return reference names", ^{ NSError *error = nil; NSArray *refs = [self.bareFixtureRepository referenceNamesWithError:&error]; - expect(refs).notTo.beNil(); - expect(error).to.beNil(); + expect(refs).notTo(beNil()); + expect(error).to(beNil()); - expect(refs.count).to.equal(4); + expect(@(refs.count)).to(equal(@4)); NSArray *expectedRefs = @[ @"refs/heads/master", @"refs/tags/v0.9", @"refs/tags/v1.0", @"refs/heads/packed" ]; - expect(refs).to.equal(expectedRefs); + expect(refs).to(equal(expectedRefs)); }); }); @@ -280,17 +399,17 @@ GTTag *tag = (GTTag *)[repo lookUpObjectBySHA:SHA error:&error]; GTOID *newOID = [repo OIDByCreatingTagNamed:@"a_new_tag" target:tag.target tagger:tag.tagger message:@"my tag\n" error:&error]; - expect(newOID).notTo.beNil(); + expect(newOID).notTo(beNil()); tag = (GTTag *)[repo lookUpObjectByOID:newOID error:&error]; - expect(error).to.beNil(); - expect(tag).notTo.beNil(); - expect(newOID.SHA).to.equal(tag.SHA); - expect(tag.type).to.equal(@"tag"); - expect(tag.message).to.equal(@"my tag\n"); - expect(tag.name).to.equal(@"a_new_tag"); - expect(tag.target.SHA).to.equal(@"5b5b025afb0b4c913b4c338a42934a3863bf3644"); - expect(tag.targetType).to.equal(GTObjectTypeCommit); + expect(error).to(beNil()); + expect(tag).notTo(beNil()); + expect(newOID.SHA).to(equal(tag.SHA)); + expect(tag.type).to(equal(@"tag")); + expect(tag.message).to(equal(@"my tag\n")); + expect(tag.name).to(equal(@"a_new_tag")); + expect(tag.target.SHA).to(equal(@"5b5b025afb0b4c913b4c338a42934a3863bf3644")); + expect(@(tag.targetType)).to(equal(@(GTObjectTypeCommit))); }); it(@"should fail to create an already existing tag", ^{ @@ -300,30 +419,190 @@ GTTag *tag = (GTTag *)[repo lookUpObjectBySHA:SHA error:&error]; GTOID *OID = [repo OIDByCreatingTagNamed:tag.name target:tag.target tagger:tag.tagger message:@"new message" error:&error]; - expect(OID).to.beNil(); - expect(error).notTo.beNil(); + expect(OID).to(beNil()); + expect(error).notTo(beNil()); }); }); +describe(@"move head", ^{ + beforeEach(^{ + repository = self.testAppFixtureRepository; + }); + + //- (BOOL)moveHEADToReference:(GTReference *)reference error:(NSError **)error; + it(@"should move to reference", ^{ + NSError *error = nil; + GTReference *originalHead = [repository headReferenceWithError:NULL]; + + GTReference *targetReference = [repository lookUpReferenceWithName:@"refs/heads/other-branch" error:NULL]; + expect(targetReference).notTo(beNil()); + + // -> Test the move + BOOL success = [repository moveHEADToReference:targetReference error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + // Verify + GTReference *head = [repository headReferenceWithError:&error]; + expect(head).notTo(beNil()); + expect(head).notTo(equal(originalHead)); + expect(head.targetOID.SHA).to(equal(targetReference.targetOID.SHA)); + }); + + //- (BOOL)moveHEADToCommit:(GTCommit *)commit error:(NSError **)error; + it(@"should move to commit", ^{ + NSError *error = nil; + GTReference *originalHead = [repository headReferenceWithError:NULL]; + NSString *targetCommitSHA = @"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"; + + GTCommit *commit = [repository lookUpObjectBySHA:targetCommitSHA error:NULL]; + expect(commit).notTo(beNil()); + + GTCommit *originalHeadCommit = [repository lookUpObjectByOID:originalHead.targetOID error:NULL]; + expect(originalHeadCommit).notTo(beNil()); + + // -> Test the move + BOOL success = [repository moveHEADToCommit:commit error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + // Test for detached? + + // Verify + GTReference *head = [repository headReferenceWithError:&error]; + expect(head).notTo(beNil()); + expect(head.targetOID.SHA).to(equal(targetCommitSHA)); + }); +}); + + describe(@"-checkout:strategy:error:progressBlock:", ^{ it(@"should allow references", ^{ NSError *error = nil; - GTReference *ref = [GTReference referenceByLookingUpReferencedNamed:@"refs/heads/other-branch" inRepository:repository error:&error]; - expect(ref).to.beTruthy(); - expect(error.localizedDescription).to.beNil(); - BOOL result = [repository checkoutReference:ref strategy:GTCheckoutStrategyAllowConflicts error:&error progressBlock:nil]; - expect(result).to.beTruthy(); - expect(error.localizedDescription).to.beNil(); + GTReference *ref = [repository lookUpReferenceWithName:@"refs/heads/other-branch" error:&error]; + expect(ref).notTo(beNil()); + expect(error.localizedDescription).to(beNil()); + BOOL result = [repository checkoutReference:ref options:[GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategyAllowConflicts] error:&error]; + expect(@(result)).to(beTruthy()); + expect(error.localizedDescription).to(beNil()); }); - + it(@"should allow commits", ^{ NSError *error = nil; GTCommit *commit = [repository lookUpObjectBySHA:@"1d69f3c0aeaf0d62e25591987b93b8ffc53abd77" objectType:GTObjectTypeCommit error:&error]; - expect(commit).to.beTruthy(); - expect(error.localizedDescription).to.beNil(); - BOOL result = [repository checkoutCommit:commit strategy:GTCheckoutStrategyAllowConflicts error:&error progressBlock:nil]; - expect(result).to.beTruthy(); - expect(error.localizedDescription).to.beNil(); + expect(commit).notTo(beNil()); + expect(error.localizedDescription).to(beNil()); + BOOL result = [repository checkoutCommit:commit options:[GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategyAllowConflicts] error:&error]; + expect(@(result)).to(beTruthy()); + expect(error.localizedDescription).to(beNil()); + }); +}); + +describe(@"-checkout:strategy:notifyFlags:error:notifyBlock:progressBlock:", ^{ + it(@"should fail ref checkout with conflict and notify", ^{ + NSError *error = nil; + GTReference *ref = [repository lookUpReferenceWithName:@"refs/heads/other-branch" error:&error]; + expect(ref).notTo(beNil()); + expect(error.localizedDescription).to(beNil()); + BOOL writeResult = [@"Conflicting data in README.md\n" writeToURL:[repository.fileURL URLByAppendingPathComponent:readmeFile] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + expect(@(writeResult)).to(beTruthy()); + __block NSUInteger notifyCount = 0; + __block BOOL readmeFileConflicted = NO; + int (^notifyBlock)(GTCheckoutNotifyFlags, NSString *, GTDiffFile *, GTDiffFile *, GTDiffFile *); + notifyBlock = ^(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir) { + notifyCount++; + if([path isEqualToString:readmeFile] && (why & GTCheckoutNotifyConflict)) { + readmeFileConflicted = YES; + } + return 0; + }; + + GTCheckoutOptions *options = [GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategySafe notifyFlags:GTCheckoutNotifyConflict notifyBlock:notifyBlock]; + BOOL result = [repository checkoutReference:ref options:options error:&error]; + expect(@(notifyCount)).to(equal(@(1))); + expect(@(readmeFileConflicted)).to(beTruthy()); + expect(@(result)).to(beFalsy()); + expect(@(error.code)).to(equal(@(GIT_ECONFLICT))); + }); + + it(@"should fail commit checkout with conflict and notify", ^{ + NSError *error = nil; + GTCommit *commit = [repository lookUpObjectBySHA:@"1d69f3c0aeaf0d62e25591987b93b8ffc53abd77" objectType:GTObjectTypeCommit error:&error]; + expect(commit).notTo(beNil()); + expect(error.localizedDescription).to(beNil()); + BOOL writeResult = [@"Conflicting data in README1.txt\n" writeToURL:[repository.fileURL URLByAppendingPathComponent:readme1File] atomically:YES encoding:NSUTF8StringEncoding error:&error]; + expect(@(writeResult)).to(beTruthy()); + __block NSUInteger notifyCount = 0; + __block BOOL readme1FileConflicted = NO; + int (^notifyBlock)(GTCheckoutNotifyFlags, NSString *, GTDiffFile *, GTDiffFile *, GTDiffFile *); + notifyBlock = ^(GTCheckoutNotifyFlags why, NSString *path, GTDiffFile *baseline, GTDiffFile *target, GTDiffFile *workdir) { + notifyCount++; + if([path isEqualToString:readme1File] && (why & GTCheckoutNotifyConflict)) { + readme1FileConflicted = YES; + } + return 0; + }; + + + GTCheckoutOptions *options = [GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategySafe notifyFlags:GTCheckoutNotifyConflict notifyBlock:notifyBlock]; + BOOL result = [repository checkoutCommit:commit options:options error:&error]; + expect(@(notifyCount)).to(equal(@(1))); + expect(@(readme1FileConflicted)).to(beTruthy()); + expect(@(result)).to(beFalsy()); + expect(@(error.code)).to(equal(@(GIT_ECONFLICT))); + }); +}); + +describe(@"-remoteNamesWithError:", ^{ + it(@"allows access to remote names", ^{ + NSError *error = nil; + NSArray *remoteNames = [repository remoteNamesWithError:&error]; + expect(error.localizedDescription).to(beNil()); + expect(remoteNames).notTo(beNil()); + }); + + it(@"returns remote names if there are any", ^{ + NSError *error = nil; + NSString *remoteName = @"testremote"; + GTRemote *remote = [GTRemote createRemoteWithName:remoteName URLString:@"git://user@example.com/testrepo" inRepository:repository error:&error]; + expect(error.localizedDescription).to(beNil()); + expect(remote).notTo(beNil()); + + NSArray *remoteNames = [repository remoteNamesWithError:&error]; + expect(error.localizedDescription).to(beNil()); + expect(remoteNames).to(contain(remoteName)); + }); +}); + +describe(@"-resetToCommit:withResetType:error:", ^{ + beforeEach(^{ + repository = self.bareFixtureRepository; + }); + + it(@"should move HEAD when used", ^{ + NSError *error = nil; + GTReference *originalHead = [repository headReferenceWithError:NULL]; + NSString *resetTargetSHA = @"8496071c1b46c854b31185ea97743be6a8774479"; + + GTCommit *commit = [repository lookUpObjectBySHA:resetTargetSHA error:NULL]; + expect(commit).notTo(beNil()); + GTCommit *originalHeadCommit = [repository lookUpObjectByOID:originalHead.targetOID error:NULL]; + expect(originalHeadCommit).notTo(beNil()); + + BOOL success = [repository resetToCommit:commit resetType:GTRepositoryResetTypeSoft error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + GTReference *head = [repository headReferenceWithError:&error]; + expect(head).notTo(beNil()); + expect(head.targetOID.SHA).to(equal(resetTargetSHA)); + + success = [repository resetToCommit:originalHeadCommit resetType:GTRepositoryResetTypeSoft error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + head = [repository headReferenceWithError:&error]; + expect(head.targetOID).to(equal(originalHead.targetOID)); }); }); @@ -333,9 +612,9 @@ BOOL success = NO; GTBranch *branch = [repository lookUpBranchWithName:@"master" type:GTBranchTypeLocal success:&success error:&error]; - expect(branch).notTo.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(branch).notTo(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); it(@"should look up a remote branch", ^{ @@ -343,9 +622,9 @@ BOOL success = NO; GTBranch *branch = [repository lookUpBranchWithName:@"origin/master" type:GTBranchTypeRemote success:&success error:&error]; - expect(branch).notTo.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(branch).notTo(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); it(@"should return nil for a nonexistent branch", ^{ @@ -353,9 +632,9 @@ BOOL success = NO; GTBranch *branch = [repository lookUpBranchWithName:@"foobar" type:GTBranchTypeLocal success:&success error:&error]; - expect(branch).to.beNil(); - expect(success).to.beTruthy(); - expect(error).to.beNil(); + expect(branch).to(beNil()); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); }); }); @@ -365,12 +644,12 @@ GTObject *obj = [repository lookUpObjectByRevParse:spec error:&error]; if (SHA != nil) { - expect(error).to.beNil(); - expect(obj).notTo.beNil(); - expect(obj.SHA).to.equal(SHA); + expect(error).to(beNil()); + expect(obj).notTo(beNil()); + expect(obj.SHA).to(equal(SHA)); } else { - expect(error).notTo.beNil(); - expect(obj).to.beNil(); + expect(error).notTo(beNil()); + expect(obj).to(beNil()); } };; @@ -388,4 +667,148 @@ }); }); -SpecEnd +describe(@"-branches:", ^{ + __block NSArray *branches; + + beforeEach(^{ + GTRepository *repository = [self testAppForkFixtureRepository]; + branches = [repository branches:NULL]; + expect(branches).notTo(beNil()); + }); + + it(@"should combine a local branch with its remote branch", ^{ + NSMutableArray *localBranches = [NSMutableArray array]; + NSMutableArray *remoteBranches = [NSMutableArray array]; + for (GTBranch *branch in branches) { + if ([branch.shortName isEqual:@"BranchA"]) { + if (branch.branchType == GTBranchTypeLocal) { + [localBranches addObject:branch]; + } else { + [remoteBranches addObject:branch]; + } + } + } + + expect(@(localBranches.count)).to(equal(@1)); + + GTBranch *localBranchA = localBranches[0]; + GTBranch *trackingBranch = [localBranchA trackingBranchWithError:NULL success:NULL]; + expect(trackingBranch.remoteName).to(equal(@"origin")); + + expect(@(remoteBranches.count)).to(equal(@1)); + + GTBranch *remoteBranchA = remoteBranches[0]; + expect(remoteBranchA.remoteName).to(equal(@"github")); + }); + + it(@"should contain local branches", ^{ + NSInteger index = [branches indexOfObjectPassingTest:^(GTBranch *branch, NSUInteger idx, BOOL *stop) { + return [branch.shortName isEqual:@"new-shite"]; + }]; + expect(@(index)).notTo(equal(@(NSNotFound))); + }); + + it(@"should contain remote branches which exist on multiple remotes", ^{ + NSUInteger matches = 0; + for (GTBranch *branch in branches) { + if ([branch.shortName isEqual:@"blah"] && branch.branchType == GTBranchTypeRemote) { + matches++; + } + } + expect(@(matches)).to(equal(@2)); + }); +}); + +describe(@"-userSignatureForNow", ^{ + static NSString * const userName = @"johnsmith"; + static NSString * const email = @"johnsmith@gmail.com"; + + __block GTConfiguration *configuration; + + beforeEach(^{ + configuration = [repository configurationWithError:NULL]; + expect(configuration).notTo(beNil()); + }); + + it(@"should use the values from the config", ^{ + [configuration setString:userName forKey:@"user.name"]; + [configuration setString:email forKey:@"user.email"]; + + GTSignature *signature = [repository userSignatureForNow]; + expect(signature.name).to(equal(userName)); + expect(signature.email).to(equal(email)); + }); + + describe(@"invalid values", ^{ + it(@"should use a default value if the name is empty", ^{ + [configuration setString:@"" forKey:@"user.name"]; + [configuration setString:email forKey:@"user.email"]; + + GTSignature *signature = [repository userSignatureForNow]; + expect(@(signature.name.length)).to(beGreaterThan(@0)); + expect(@(signature.email.length)).to(beGreaterThan(@0)); + }); + + it(@"should use a default value if the email is empty", ^{ + [configuration setString:userName forKey:@"user.name"]; + [configuration setString:@"" forKey:@"user.email"]; + + GTSignature *signature = [repository userSignatureForNow]; + expect(@(signature.name.length)).to(beGreaterThan(@0)); + expect(@(signature.email.length)).to(beGreaterThan(@0)); + }); + + it(@"should use a default value if the email contains angled brackets", ^{ + [configuration setString:userName forKey:@"user.name"]; + [configuration setString:@"" forKey:@"user.email"]; + + GTSignature *signature = [repository userSignatureForNow]; + expect(@(signature.name.length)).to(beGreaterThan(@0)); + expect(@(signature.email.length)).to(beGreaterThan(@0)); + }); + }); +}); + +describe(@"-calculateState:withError:", ^{ + it(@"should find if the repository is mid-merge", ^{ + GTRepository *repository = [self conflictedFixtureRepository]; + GTRepositoryStateType state; + BOOL result; + result = [repository calculateState:&state withError:NULL]; + expect(@(result)).to(beTruthy()); + expect(@(state)).to(equal(@(GTRepositoryStateMerge))); + }); + + it(@"should return none otherwise", ^{ + GTRepository *repository = [self testAppFixtureRepository]; + GTRepositoryStateType state; + BOOL result; + result = [repository calculateState:&state withError:NULL]; + expect(@(result)).to(beTruthy()); + expect(@(state)).to(equal(@(GTRepositoryStateNone))); + }); +}); + +describe(@"-cleanupStateWithError:", ^{ + it(@"should return a repository to a pre-merge state", ^{ + GTRepository *repository = [self conflictedFixtureRepository]; + + GTRepositoryStateType state; + BOOL result; + result = [repository calculateState:&state withError:NULL]; + expect(@(result)).to(beTruthy()); + expect(@(state)).to(equal(@(GTRepositoryStateMerge))); + + expect(@([repository cleanupStateWithError:NULL])).to(beTruthy()); + + result = [repository calculateState:&state withError:NULL]; + expect(@(result)).to(beTruthy()); + expect(@(state)).to(equal(@(GTRepositoryStateNone))); + }); +}); + +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTRepositoryStashingSpec.m b/ObjectiveGitTests/GTRepositoryStashingSpec.m index 892083dcb..4b5891e56 100644 --- a/ObjectiveGitTests/GTRepositoryStashingSpec.m +++ b/ObjectiveGitTests/GTRepositoryStashingSpec.m @@ -6,25 +6,29 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTRepository+Stashing.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTRepositoryStashing) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTRepositoryStashing) __block GTRepository *repository; beforeEach(^{ repository = self.testAppFixtureRepository; - expect(repository).notTo.beNil(); + expect(repository).notTo(beNil()); }); it(@"should fail to create a stash if there's nothing to stash", ^{ NSError *error = nil; GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error]; - expect(stash).to.beNil(); + expect(stash).to(beNil()); - expect(error).notTo.beNil(); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_ENOTFOUND); + expect(error).notTo(beNil()); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_ENOTFOUND))); }); it(@"should create a stash with modified file content", ^{ @@ -32,17 +36,17 @@ NSString *newContent = @"foobar"; NSString *oldContent = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]; - expect(oldContent).notTo.equal(newContent); + expect(oldContent).notTo(equal(newContent)); - expect([newContent writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]).to.beTruthy(); - expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to.equal(newContent); + expect(@([newContent writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); + expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to(equal(newContent)); NSError *error = nil; GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error]; - expect(stash).notTo.beNil(); - expect(error).to.beNil(); + expect(stash).notTo(beNil()); + expect(error).to(beNil()); - expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to.equal(oldContent); + expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to(equal(oldContent)); }); it(@"should create a stash with uncommitted changes", ^{ @@ -50,38 +54,38 @@ NSString *newContent = @"foobar"; NSString *oldContent = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]; - expect(oldContent).notTo.equal(newContent); + expect(oldContent).notTo(equal(newContent)); - expect([newContent writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]).to.beTruthy(); - expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to.equal(newContent); + expect(@([newContent writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); + expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to(equal(newContent)); NSError *error = nil; GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error]; - expect(stash).notTo.beNil(); - expect(error).to.beNil(); + expect(stash).notTo(beNil()); + expect(error).to(beNil()); - expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to.equal(oldContent); + expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to(equal(oldContent)); }); it(@"should fail to create a stash with an untracked file using default options", ^{ - expect([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL]).to.beTruthy(); + expect(@([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); NSError *error = nil; GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error]; - expect(stash).to.beNil(); + expect(stash).to(beNil()); - expect(error).notTo.beNil(); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_ENOTFOUND); + expect(error).notTo(beNil()); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_ENOTFOUND))); }); it(@"should stash an untracked file when enabled", ^{ - expect([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL]).to.beTruthy(); + expect(@([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); NSError *error = nil; GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagIncludeUntracked error:&error]; - expect(stash).notTo.beNil(); - expect(error).to.beNil(); + expect(stash).notTo(beNil()); + expect(error).to(beNil()); }); it(@"should enumerate stashes", ^{ @@ -90,14 +94,14 @@ for (int i = stashCount; i >= 0; i--) { NSString *filename = [NSString stringWithFormat:@"new-test-file-%i", i]; - expect([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:filename] atomically:YES encoding:NSUTF8StringEncoding error:NULL]).to.beTruthy(); + expect(@([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:filename] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); NSString *message = [NSString stringWithFormat:@"stash %i", i]; NSError *error = nil; GTCommit *stash = [repository stashChangesWithMessage:message flags:GTRepositoryStashFlagIncludeUntracked error:&error]; - expect(stash).notTo.beNil(); - expect(error).to.beNil(); + expect(stash).notTo(beNil()); + expect(error).to(beNil()); [stashCommits insertObject:stash atIndex:0]; } @@ -107,13 +111,91 @@ lastIndex = i; NSString *expectedMessage = [NSString stringWithFormat:@"On master: stash %lu", (unsigned long)i]; - expect(oid).to.equal([stashCommits[i] OID]); - expect(message).to.equal(expectedMessage); + expect(oid).to(equal([stashCommits[i] OID])); + expect(message).to(equal(expectedMessage)); if (i == 2) *stop = YES; }]; - expect(lastIndex).to.equal(2); + expect(@(lastIndex)).to(equal(@2)); +}); + +it(@"should apply stashes", ^{ + expect(@([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); + + NSError *error = nil; + GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagIncludeUntracked error:&error]; + expect(stash).notTo(beNil()); + expect(error).to(beNil()); + + __block BOOL progressCalled = NO; + BOOL success = [repository applyStashAtIndex:0 flags:GTRepositoryStashApplyFlagDefault checkoutOptions:nil error:&error progressBlock:^void(GTRepositoryStashApplyProgress step, BOOL *stop) { + progressCalled = YES; + }]; + expect(@(success)).to(beTruthy()); + expect(@(progressCalled)).to(beTruthy()); + expect(error).to(beNil()); + + expect([NSString stringWithContentsOfURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] encoding:NSUTF8StringEncoding error:NULL]).to(equal(@"foobar")); +}); + + +it(@"should drop stashes", ^{ + expect(@([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); + + NSError *error = nil; + GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagIncludeUntracked error:&error]; + expect(stash).notTo(beNil()); + expect(error).to(beNil()); + + BOOL success = [repository dropStashAtIndex:0 error:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); +}); + +it(@"should fail to apply/drop unknown stashes", ^{ + NSError *error = nil; + BOOL success = NO; + + __block NSUInteger lastStashIndex = 0; + [repository enumerateStashesUsingBlock:^(NSUInteger index, NSString * _Nullable message, GTOID * _Nullable oid, BOOL * _Nonnull stop) { + lastStashIndex = index; + }]; + + success = [repository applyStashAtIndex:(lastStashIndex + 1) flags:GTRepositoryStashApplyFlagDefault checkoutOptions:nil error:&error progressBlock:nil]; + expect(@(success)).to(beFalsy()); + expect(error).notTo(beNil()); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_ENOTFOUND))); + + success = [repository dropStashAtIndex:(lastStashIndex + 1) error:&error]; + expect(@(success)).to(beFalsy()); + expect(error).notTo(beNil()); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_ENOTFOUND))); +}); + +it(@"should fail to apply conflicting stashes", ^{ + expect(@([@"foobar" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); + + NSError *error = nil; + GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagIncludeUntracked error:&error]; + expect(stash).notTo(beNil()); + expect(error).to(beNil()); + + + expect(@([@"barfoo" writeToURL:[repository.fileURL URLByAppendingPathComponent:@"new-test-file"] atomically:YES encoding:NSUTF8StringEncoding error:NULL])).to(beTruthy()); + + BOOL success = [repository applyStashAtIndex:0 flags:GTRepositoryStashApplyFlagDefault checkoutOptions:nil error:&error progressBlock:nil]; + expect(@(success)).to(beFalsy()); + expect(error).notTo(beNil()); + + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_ECONFLICT))); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTSignatureSpec.m b/ObjectiveGitTests/GTSignatureSpec.m index 0a2a23af4..17281a0b1 100644 --- a/ObjectiveGitTests/GTSignatureSpec.m +++ b/ObjectiveGitTests/GTSignatureSpec.m @@ -6,7 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -SpecBegin(GTSignature) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTSignatureSpec) NSString *name = @"test_user"; NSString *email = @"test@example.com"; @@ -22,20 +28,20 @@ beforeEach(^{ testSignature = [[GTSignature alloc] initWithName:name email:email time:time]; - expect(testSignature).notTo.beNil(); + expect(testSignature).notTo(beNil()); }); it(@"should expose the git_signature", ^{ - expect(testSignature.git_signature).notTo.beNil(); - expect(testSignature).to.equal([[GTSignature alloc] initWithGitSignature:testSignature.git_signature]); + expect([NSValue valueWithPointer:testSignature.git_signature]).notTo(equal([NSValue valueWithPointer:NULL])); + expect(testSignature).to(equal([[GTSignature alloc] initWithGitSignature:testSignature.git_signature])); }); it(@"should compare equal to a signature created with the same information", ^{ - expect(testSignature).to.equal([[GTSignature alloc] initWithName:name email:email time:time]); + expect(testSignature).to(equal([[GTSignature alloc] initWithName:name email:email time:time])); }); it(@"should compare unequal to a different signature", ^{ - expect(testSignature).notTo.equal([[GTSignature alloc] initWithName:name email:email time:[NSDate dateWithTimeIntervalSinceNow:10]]); + expect(testSignature).notTo(equal([[GTSignature alloc] initWithName:name email:email time:[NSDate dateWithTimeIntervalSinceNow:10]])); }); }); @@ -43,13 +49,17 @@ const git_signature *git_signature = NULL; { - GTSignature *testSignature __attribute__((objc_precise_lifetime)) = [[GTSignature alloc] initWithName:name email:email time:time]; + GTSignature *testSignature = [[GTSignature alloc] initWithName:name email:email time:time]; git_signature = testSignature.git_signature; } GTSignature *testSignature = [[GTSignature alloc] initWithGitSignature:git_signature]; - expect(testSignature.name).to.equal(name); - expect(testSignature.email).to.equal(email); + expect(testSignature.name).to(equal(name)); + expect(testSignature.email).to(equal(email)); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTSubmoduleSpec.m b/ObjectiveGitTests/GTSubmoduleSpec.m index 23319b690..0a25f5e35 100644 --- a/ObjectiveGitTests/GTSubmoduleSpec.m +++ b/ObjectiveGitTests/GTSubmoduleSpec.m @@ -6,43 +6,45 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -SpecBegin(GTSubmodule) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTSubmoduleSpec) __block GTRepository *repo; beforeEach(^{ repo = self.submoduleFixtureRepository; - expect(repo).notTo.beNil(); + expect(repo).notTo(beNil()); }); it(@"should enumerate top-level submodules", ^{ NSMutableSet *names = [NSMutableSet set]; [repo enumerateSubmodulesRecursively:NO usingBlock:^(GTSubmodule *submodule, NSError *error, BOOL *stop) { - expect(stop).notTo.beNil(); - - expect(submodule).to.beKindOf(GTSubmodule.class); - expect(submodule.name).notTo.beNil(); + expect(submodule).to(beAnInstanceOf(GTSubmodule.class)); + expect(submodule.name).notTo(beNil()); [names addObject:submodule.name]; }]; NSSet *expectedNames = [NSSet setWithArray:@[ @"Archimedes", @"Test_App", @"Test_App2" ]]; - expect(names).to.equal(expectedNames); + expect(names).to(equal(expectedNames)); }); it(@"should enumerate submodules recursively", ^{ NSMutableSet *names = [NSMutableSet set]; [repo enumerateSubmodulesRecursively:YES usingBlock:^(GTSubmodule *submodule, NSError *error, BOOL *stop) { - expect(stop).notTo.beNil(); - - expect(submodule).to.beKindOf(GTSubmodule.class); - expect(submodule.name).notTo.beNil(); + expect(submodule).to(beAnInstanceOf(GTSubmodule.class)); + expect(submodule.name).notTo(beNil()); [names addObject:submodule.name]; }]; NSSet *expectedNames = [NSSet setWithArray:@[ @"Archimedes", @"Configuration", @"ArchimedesTests/expecta", @"ArchimedesTests/specta", @"Test_App", @"Test_App2" ]]; - expect(names).to.equal(expectedNames); + expect(names).to(equal(expectedNames)); }); it(@"should terminate enumeration early", ^{ @@ -55,61 +57,50 @@ } }]; - expect(count).to.equal(2); + expect(@(count)).to(equal(@2)); }); it(@"should write to the parent .git/config", ^{ - NSString *testURLString = @"fake_url"; + NSString *testURLString = @"git://fake_url"; GTSubmodule *submodule = [repo submoduleWithName:@"Test_App" error:NULL]; - expect(submodule).notTo.beNil(); - expect(@(git_submodule_url(submodule.git_submodule))).notTo.equal(testURLString); + expect(submodule).notTo(beNil()); + expect(@(git_submodule_url(submodule.git_submodule))).notTo(equal(testURLString)); - git_submodule_set_url(submodule.git_submodule, testURLString.UTF8String); - git_submodule_save(submodule.git_submodule); + git_submodule_set_url(repo.git_repository, git_submodule_name(submodule.git_submodule), testURLString.UTF8String); __block NSError *error = nil; - expect([submodule writeToParentConfigurationDestructively:YES error:&error]).to.beTruthy(); - expect(error).to.beNil(); + expect(@([submodule writeToParentConfigurationDestructively:YES error:&error])).to(beTruthy()); + expect(error).to(beNil()); submodule = [repo submoduleWithName:@"Test_App" error:NULL]; - expect(submodule).notTo.beNil(); - expect(@(git_submodule_url(submodule.git_submodule))).to.equal(testURLString); -}); - -it(@"should reload all submodules", ^{ - GTSubmodule *submodule = [repo submoduleWithName:@"new_submodule" error:NULL]; - expect(submodule).to.beNil(); - - NSURL *gitmodulesURL = [repo.fileURL URLByAppendingPathComponent:@".gitmodules"]; - NSMutableString *gitmodules = [NSMutableString stringWithContentsOfURL:gitmodulesURL usedEncoding:NULL error:NULL]; - expect(gitmodules).notTo.beNil(); - - [gitmodules appendString:@"[submodule \"new_submodule\"]\n\turl = some_url\n\tpath = new_submodule_path"]; - expect([gitmodules writeToURL:gitmodulesURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]).to.beTruthy(); - - __block NSError *error = nil; - expect([repo reloadSubmodules:&error]).to.beTruthy(); - expect(error).to.beNil(); - - submodule = [repo submoduleWithName:@"new_submodule" error:NULL]; - expect(submodule).notTo.beNil(); - expect(submodule.path).to.equal(@"new_submodule_path"); + expect(submodule).notTo(beNil()); + expect(@(git_submodule_url(submodule.git_submodule))).to(equal(testURLString)); }); it(@"should add its HEAD to its parent's index", ^{ GTSubmodule *submodule = [repo submoduleWithName:@"Test_App" error:NULL]; - expect(submodule).notTo.beNil(); + expect(submodule).notTo(beNil()); GTRepository *submoduleRepository = [[GTRepository alloc] initWithURL:[repo.fileURL URLByAppendingPathComponent:submodule.path] error:NULL]; - expect(submoduleRepository).notTo.beNil(); + expect(submoduleRepository).notTo(beNil()); GTCommit *commit = [submoduleRepository lookUpObjectByRevParse:@"HEAD^" error:NULL]; - BOOL success = [submoduleRepository checkoutCommit:commit strategy:GTCheckoutStrategyForce error:NULL progressBlock:nil]; - expect(success).to.beTruthy(); + BOOL success = [submoduleRepository checkoutCommit:commit options:[GTCheckoutOptions checkoutOptionsWithStrategy:GTCheckoutStrategyForce] error:NULL]; + expect(@(success)).to(beTruthy()); success = [submodule addToIndex:NULL]; - expect(success).to.beTruthy(); + expect(@(success)).to(beTruthy()); +}); + +it(@"should update the ignore rule", ^{ + GTSubmodule *submodule = [repo submoduleWithName:@"Test_App" error:NULL]; + expect(submodule).notTo(beNil()); + expect(@(submodule.ignoreRule)).to(equal(@(GTSubmoduleIgnoreNone))); + + GTSubmodule *updatedSubmodule = [submodule submoduleByUpdatingIgnoreRule:GTSubmoduleIgnoreAll error:NULL]; + expect(@(updatedSubmodule.ignoreRule)).to(equal(@(GTSubmoduleIgnoreAll))); + expect(@(submodule.ignoreRule)).to(equal(@(GTSubmoduleIgnoreNone))); }); describe(@"clean, checked out submodule", ^{ @@ -118,66 +109,66 @@ beforeEach(^{ NSError *error = nil; submodule = [repo submoduleWithName:@"Test_App" error:&error]; - expect(submodule).notTo.beNil(); - expect(error).to.beNil(); - - expect(submodule.name).to.equal(@"Test_App"); - expect(submodule.path).to.equal(@"Test_App"); - expect(submodule.URLString).to.equal(@"../Test_App"); - expect(submodule.parentRepository).to.beIdenticalTo(repo); - expect(submodule.git_submodule).notTo.beNil(); + expect(submodule).notTo(beNil()); + expect(error).to(beNil()); + + expect(submodule.name).to(equal(@"Test_App")); + expect(submodule.path).to(equal(@"Test_App")); + expect(submodule.URLString).to(equal(@"../Test_App")); + expect(submodule.parentRepository).to(beIdenticalTo(repo)); + expect([NSValue valueWithPointer:submodule.git_submodule]).notTo(equal([NSValue valueWithPointer:NULL])); }); it(@"should compare equal to the same submodule", ^{ - expect(submodule).to.equal([repo submoduleWithName:@"Test_App" error:NULL]); + expect(submodule).to(equal([repo submoduleWithName:@"Test_App" error:NULL])); }); it(@"should compare unequal to a different submodule", ^{ - expect(submodule).notTo.equal([repo submoduleWithName:@"Test_App2" error:NULL]); + expect(submodule).notTo(equal([repo submoduleWithName:@"Test_App2" error:NULL])); }); it(@"should have identical OIDs", ^{ - expect(submodule.HEADOID.SHA).to.equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"); - expect(submodule.indexOID.SHA).to.equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"); - expect(submodule.workingDirectoryOID.SHA).to.equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0"); + expect(submodule.HEADOID.SHA).to(equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0")); + expect(submodule.indexOID.SHA).to(equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0")); + expect(submodule.workingDirectoryOID.SHA).to(equal(@"f7ecd8f4404d3a388efbff6711f1bdf28ffd16a0")); }); it(@"should have a clean status", ^{ GTSubmoduleStatus expectedStatus = GTSubmoduleStatusExistsInHEAD | GTSubmoduleStatusExistsInIndex | GTSubmoduleStatusExistsInConfig | GTSubmoduleStatusExistsInWorkingDirectory; __block NSError *error = nil; - expect([submodule status:&error]).to.equal(expectedStatus); - expect(error).to.beNil(); + expect(@([submodule status:&error])).to(equal(@(expectedStatus))); + expect(error).to(beNil()); }); it(@"should open a repository" ,^{ NSError *error = nil; GTRepository *submoduleRepo = [submodule submoduleRepository:&error]; - expect(submoduleRepo).notTo.beNil(); - expect(error).to.beNil(); - - expect(submoduleRepo.fileURL).to.equal([repo.fileURL URLByAppendingPathComponent:@"Test_App"]); - expect(submoduleRepo.bare).to.beFalsy(); - expect(submoduleRepo.empty).to.beFalsy(); - expect(submoduleRepo.HEADDetached).to.beTruthy(); - expect([submoduleRepo isWorkingDirectoryClean]).to.beTruthy(); + expect(submoduleRepo).notTo(beNil()); + expect(error).to(beNil()); + + expect(submoduleRepo.fileURL).to(equal([repo.fileURL URLByAppendingPathComponent:@"Test_App"])); + expect(@(submoduleRepo.bare)).to(beFalsy()); + expect(@(submoduleRepo.empty)).to(beFalsy()); + expect(@(submoduleRepo.HEADDetached)).to(beTruthy()); + expect(@([submoduleRepo isWorkingDirectoryClean])).to(beTruthy()); }); it(@"should reload", ^{ GTRepository *submoduleRepo = [submodule submoduleRepository:NULL]; - expect(submoduleRepo).notTo.beNil(); + expect(submoduleRepo).notTo(beNil()); GTCommit *newHEAD = (id)[submoduleRepo lookUpObjectBySHA:@"82dc47f6ba3beecab33080a1136d8913098e1801" objectType:GTObjectTypeCommit error:NULL]; - expect(newHEAD).notTo.beNil(); - expect([submoduleRepo resetToCommit:newHEAD resetType:GTRepositoryResetTypeHard error:NULL]).to.beTruthy(); + expect(newHEAD).notTo(beNil()); + expect(@([submoduleRepo resetToCommit:newHEAD resetType:GTRepositoryResetTypeHard error:NULL])).to(beTruthy()); - expect(submodule.workingDirectoryOID.SHA).notTo.equal(newHEAD.SHA); + expect(submodule.workingDirectoryOID.SHA).notTo(equal(newHEAD.SHA)); __block NSError *error = nil; - expect([submodule reload:&error]).to.beTruthy(); - expect(error).to.beNil(); + expect(@([submodule reload:&error])).to(beTruthy()); + expect(error).to(beNil()); - expect(submodule.workingDirectoryOID.SHA).to.equal(newHEAD.SHA); + expect(submodule.workingDirectoryOID.SHA).to(equal(newHEAD.SHA)); }); }); @@ -187,28 +178,28 @@ beforeEach(^{ NSError *error = nil; submodule = [repo submoduleWithName:@"Test_App2" error:&error]; - expect(submodule).notTo.beNil(); - expect(error).to.beNil(); - - expect(submodule.name).to.equal(@"Test_App2"); - expect(submodule.path).to.equal(@"Test_App2"); - expect(submodule.URLString).to.equal(@"../Test_App"); - expect(submodule.parentRepository).to.beIdenticalTo(repo); - expect(submodule.git_submodule).notTo.beNil(); + expect(submodule).notTo(beNil()); + expect(error).to(beNil()); + + expect(submodule.name).to(equal(@"Test_App2")); + expect(submodule.path).to(equal(@"Test_App2")); + expect(submodule.URLString).to(equal(@"../Test_App")); + expect(submodule.parentRepository).to(beIdenticalTo(repo)); + expect([NSValue valueWithPointer:submodule.git_submodule]).notTo(equal([NSValue valueWithPointer:NULL])); }); it(@"should compare equal to the same submodule", ^{ - expect(submodule).to.equal([repo submoduleWithName:@"Test_App2" error:NULL]); + expect(submodule).to(equal([repo submoduleWithName:@"Test_App2" error:NULL])); }); it(@"should compare unequal to a different submodule", ^{ - expect(submodule).notTo.equal([repo submoduleWithName:@"Test_App" error:NULL]); + expect(submodule).notTo(equal([repo submoduleWithName:@"Test_App" error:NULL])); }); it(@"should have varying OIDs", ^{ - expect(submodule.HEADOID.SHA).to.equal(@"a4bca6b67a5483169963572ee3da563da33712f7"); - expect(submodule.indexOID.SHA).to.equal(@"93f5b550149f9f4c702c9de9a8b0a8a357f0c41c"); - expect(submodule.workingDirectoryOID.SHA).to.equal(@"1d69f3c0aeaf0d62e25591987b93b8ffc53abd77"); + expect(submodule.HEADOID.SHA).to(equal(@"a4bca6b67a5483169963572ee3da563da33712f7")); + expect(submodule.indexOID.SHA).to(equal(@"93f5b550149f9f4c702c9de9a8b0a8a357f0c41c")); + expect(submodule.workingDirectoryOID.SHA).to(equal(@"1d69f3c0aeaf0d62e25591987b93b8ffc53abd77")); }); it(@"should have a dirty status", ^{ @@ -218,50 +209,49 @@ GTSubmoduleStatusDirtyIndex | GTSubmoduleStatusDirtyWorkingDirectory | GTSubmoduleStatusUntrackedFilesInWorkingDirectory; __block NSError *error = nil; - expect([submodule status:&error]).to.equal(expectedStatus); - expect(error).to.beNil(); + expect(@([submodule status:&error])).to(equal(@(expectedStatus))); + expect(error).to(beNil()); }); it(@"should honor the ignore rule", ^{ - submodule.ignoreRule = GTSubmoduleIgnoreDirty; - GTSubmoduleStatus expectedStatus = GTSubmoduleStatusExistsInHEAD | GTSubmoduleStatusExistsInIndex | GTSubmoduleStatusExistsInConfig | GTSubmoduleStatusExistsInWorkingDirectory | GTSubmoduleStatusModifiedInIndex | GTSubmoduleStatusModifiedInWorkingDirectory; - expect([submodule status:NULL]).to.equal(expectedStatus); + expect(@([submodule statusWithIgnoreRule:GTSubmoduleIgnoreDirty error:NULL])).to(equal(@(expectedStatus))); }); it(@"should open a repository" ,^{ NSError *error = nil; GTRepository *submoduleRepo = [submodule submoduleRepository:&error]; - expect(submoduleRepo).notTo.beNil(); - expect(error).to.beNil(); - - expect(submoduleRepo.fileURL).to.equal([repo.fileURL URLByAppendingPathComponent:@"Test_App2"]); - expect(submoduleRepo.bare).to.beFalsy(); - expect(submoduleRepo.empty).to.beFalsy(); - expect(submoduleRepo.HEADDetached).to.beTruthy(); - expect([submoduleRepo isWorkingDirectoryClean]).to.beFalsy(); + expect(submoduleRepo).notTo(beNil()); + expect(error).to(beNil()); + + expect(submoduleRepo.fileURL).to(equal([repo.fileURL URLByAppendingPathComponent:@"Test_App2"])); + expect(@(submoduleRepo.bare)).to(beFalsy()); + expect(@(submoduleRepo.empty)).to(beFalsy()); + expect(@(submoduleRepo.HEADDetached)).to(beTruthy()); + expect(@([submoduleRepo isWorkingDirectoryClean])).to(beFalsy()); }); it(@"should synchronize the remote URL", ^{ GTConfiguration *config = [repo configurationWithError:NULL]; - expect(config).notTo.beNil(); + expect(config).notTo(beNil()); NSString *configKey = @"submodule.Test_App2.url"; NSString *newOrigin = @"https://github.com/libgit2/objective-git.git"; - [config setString:newOrigin forKey:configKey]; - expect([config refresh:NULL]).to.beTruthy(); - expect([config stringForKey:configKey]).to.equal(newOrigin); + [config setString:newOrigin forKey:configKey]; + expect([config stringForKey:configKey]).to(equal(newOrigin)); __block NSError *error = nil; - expect([submodule sync:&error]).to.beTruthy(); - - expect([config refresh:NULL]).to.beTruthy(); - expect([config stringForKey:configKey]).to.equal(@"../Test_App"); + expect(@([submodule sync:&error])).to(beTruthy()); + expect([config stringForKey:configKey]).to(equal(@"../Test_App")); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTTagSpec.m b/ObjectiveGitTests/GTTagSpec.m index 6585174d2..bdbb5a5b4 100644 --- a/ObjectiveGitTests/GTTagSpec.m +++ b/ObjectiveGitTests/GTTagSpec.m @@ -6,9 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTTag.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTTag) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTTagSpec) __block GTTag *tag; @@ -17,22 +21,38 @@ GTRepository *repo = self.bareFixtureRepository; NSString *tagSHA = @"0c37a5391bbff43c37f0d0371823a5509eed5b1d"; tag = (GTTag *)[repo lookUpObjectBySHA:tagSHA error:&error]; - expect(error).to.beFalsy(); - expect(tag).to.beTruthy(); - expect(tagSHA).to.equal(tag.SHA); + expect(error).to(beNil()); + expect(tag).notTo(beNil()); + expect(tagSHA).to(equal(tag.SHA)); }); it(@"can read tag data", ^{ - expect(tag.type).to.equal(@"tag"); - expect(tag.name).to.equal(@"v1.0"); - expect(tag.message).to.equal(@"test tag message\n"); - expect(tag.target.SHA).to.equal(@"5b5b025afb0b4c913b4c338a42934a3863bf3644"); - expect(GTObjectTypeCommit).to.equal(tag.targetType); - + expect(tag.type).to(equal(@"tag")); + expect(tag.name).to(equal(@"v1.0")); + expect(tag.message).to(equal(@"test tag message\n")); + expect(tag.target.SHA).to(equal(@"5b5b025afb0b4c913b4c338a42934a3863bf3644")); + expect(@(GTObjectTypeCommit)).to(equal(@(tag.targetType))); + GTSignature *signature = tag.tagger; - expect(signature.name).to.equal(@"Scott Chacon"); - expect((int)[signature.time timeIntervalSince1970]).to.equal(1288114383); - expect(signature.email).to.equal(@"schacon@gmail.com"); + expect(signature.name).to(equal(@"Scott Chacon")); + expect(@(signature.time.timeIntervalSince1970)).to(equal(@1288114383)); + expect(signature.email).to(equal(@"schacon@gmail.com")); +}); + +it(@"can delete tags", ^{ + NSError *error = nil; + + BOOL success = [tag delete:&error]; + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + success = [tag delete:&error]; + expect(@(success)).to(beFalsy()); + expect(error).notTo(beNil()); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTTimeAdditionsSpec.m b/ObjectiveGitTests/GTTimeAdditionsSpec.m index 11d387927..21c649141 100644 --- a/ObjectiveGitTests/GTTimeAdditionsSpec.m +++ b/ObjectiveGitTests/GTTimeAdditionsSpec.m @@ -6,9 +6,13 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "NSDate+GTTimeAdditions.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(GTTimeAdditions) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(GTTimeAdditions) describe(@"Conversion between git_time and NSDate", ^{ it(@"should be able to create a correct NSDate and NSTimeZone when given a git_time", ^{ @@ -16,39 +20,33 @@ int offset = -120; //2 hours behind GMT git_time time = (git_time){ .time = seconds, .offset = offset }; NSDate *date = [NSDate gt_dateFromGitTime:time]; - expect(date).toNot.beNil(); - - NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; + expect(date).notTo(beNil()); + + NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; gregorianCalendar.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; - NSDateComponents *components = [gregorianCalendar components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit fromDate:date]; - expect(components).toNot.beNil(); - - expect(components.day).to.equal(5); - expect(components.month).to.equal(2); - expect(components.year).to.equal(2010); - expect(components.hour).to.equal(13); - + NSDateComponents *components = [gregorianCalendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour fromDate:date]; + expect(components).notTo(beNil()); + + expect(@(components.day)).to(equal(@5)); + expect(@(components.month)).to(equal(@2)); + expect(@(components.year)).to(equal(@2010)); + expect(@(components.hour)).to(equal(@13)); + NSTimeZone *timeZone = [NSTimeZone gt_timeZoneFromGitTime:time]; - expect(timeZone).toNot.beNil(); + expect(timeZone).notTo(beNil()); NSInteger expectedSecondsFromGMT = -120 * 60; - expect(timeZone.secondsFromGMT).to.equal(expectedSecondsFromGMT); + expect(@(timeZone.secondsFromGMT)).to(equal(@(expectedSecondsFromGMT))); }); - + it(@"should return a correct offset for an NSTimeZone", ^{ NSTimeZone *timeZone = [NSTimeZone timeZoneForSecondsFromGMT:180 * 60]; - expect(timeZone).toNot.beNil(); - expect(timeZone.gt_gitTimeOffset).to.equal(180); - }); - - it(@"should return a correct git_time for an NSDate", ^{ - NSDate *date = [NSDate dateWithString:@"2010-05-12 18:29:13 +0000"]; - expect(date).toNot.beNil(); - - NSTimeZone *twoHoursAheadOfGMT = [NSTimeZone timeZoneForSecondsFromGMT:120 * 60]; - git_time time = [date gt_gitTimeUsingTimeZone:twoHoursAheadOfGMT]; - expect(time.time).to.equal(1273688953); - expect(time.offset).to.equal(120); + expect(timeZone).notTo(beNil()); + expect(@(timeZone.gt_gitTimeOffset)).to(equal(@180)); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTTreeBuilderSpec.m b/ObjectiveGitTests/GTTreeBuilderSpec.m index f9a42bbc3..4c0d9a3b1 100644 --- a/ObjectiveGitTests/GTTreeBuilderSpec.m +++ b/ObjectiveGitTests/GTTreeBuilderSpec.m @@ -6,139 +6,135 @@ // Copyright (c) 2013 Johnnie Walker // -#import "GTTree.h" -#import "GTTreeEntry.h" -#import "GTTreeBuilder.h" -#import "GTRepository.h" -#import "GTBlob.h" -#import "git2.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" static NSString * const testTreeSHA = @"c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"; -SpecBegin(GTTreeBuilder) +QuickSpecBegin(GTTreeBuilderSpec) + +__block GTRepository *repo; + +beforeEach(^{ + repo = self.bareFixtureRepository; + expect(repo).notTo(beNil()); +}); it(@"should be possible to make a new tree builder without a tree", ^{ NSError *error = nil; - GTTreeBuilder *builder = [[GTTreeBuilder alloc] initWithTree:nil error:&error]; - expect(error).to.beNil(); - expect(builder).notTo.beNil(); + GTTreeBuilder *builder = [[GTTreeBuilder alloc] initWithTree:nil repository:repo error:&error]; + expect(error).to(beNil()); + expect(builder).notTo(beNil()); }); it(@"should be possible to make a new tree builder from an existing tree", ^{ NSError *error = nil; - - GTRepository *repo = self.bareFixtureRepository; - expect(repo).notTo.beNil(); - + GTTree *tree = (GTTree *)[repo lookUpObjectBySHA:testTreeSHA error:NULL]; - expect(tree).notTo.beNil(); - - GTTreeBuilder *builder = [[GTTreeBuilder alloc] initWithTree:tree error:&error]; - expect(error).to.beNil(); - expect(builder).notTo.beNil(); + expect(tree).notTo(beNil()); + + GTTreeBuilder *builder = [[GTTreeBuilder alloc] initWithTree:tree repository:repo error:&error]; + expect(error).to(beNil()); + expect(builder).notTo(beNil()); }); describe(@"GTTreeBuilder building", ^{ __block GTTreeBuilder *builder; __block NSError *error = nil; __block GTOID *OID; - + beforeEach(^{ - builder = [[GTTreeBuilder alloc] initWithTree:nil error:&error]; - expect(builder).notTo.beNil(); - expect(error).to.beNil(); + builder = [[GTTreeBuilder alloc] initWithTree:nil repository:repo error:&error]; + expect(builder).notTo(beNil()); + expect(error).to(beNil()); OID = [GTOID oidWithSHA:testTreeSHA]; }); - + it(@"should be possible to add an entry to a builder", ^{ GTTreeEntry *entry = [builder addEntryWithOID:OID fileName:@"tree" fileMode:GTFileModeTree error:&error]; - expect(entry).notTo.beNil(); - expect(error).to.beNil(); - - expect(builder.entryCount).to.equal(1); + expect(entry).notTo(beNil()); + expect(error).to(beNil()); + + expect(@(builder.entryCount)).to(equal(@1)); }); - + it(@"should be possible to remove an entry from a builder", ^{ NSString *fileName = @"tree"; GTTreeEntry *entry = [builder addEntryWithOID:OID fileName:fileName fileMode:GTFileModeTree error:&error]; - expect(entry).notTo.beNil(); - expect(error).to.beNil(); - - expect(builder.entryCount).to.equal(1); - + expect(entry).notTo(beNil()); + expect(error).to(beNil()); + + expect(@(builder.entryCount)).to(equal(@1)); + BOOL success = [builder removeEntryWithFileName:fileName error:&error]; - expect(success).to.beTruthy(); - expect(error).to.beNil(); - - expect(builder.entryCount).to.equal(0); + expect(@(success)).to(beTruthy()); + expect(error).to(beNil()); + + expect(@(builder.entryCount)).to(equal(@0)); }); - - it(@"should be possible to filter a builder", ^{ - GTRepository *repo = self.bareFixtureRepository; - expect(repo).notTo.beNil(); - + + it(@"should be possible to filter a builder", ^{ GTBlob *blob = [GTBlob blobWithString:@"Hi, how are you?" inRepository:repo error:&error]; - expect(blob).notTo.beNil(); - expect(error).to.beNil(); - + expect(blob).notTo(beNil()); + expect(error).to(beNil()); + [builder addEntryWithOID:blob.OID fileName:@"hi.txt" fileMode:GTFileModeBlob error:&error]; - - expect(builder.entryCount).to.equal(1); - + + expect(@(builder.entryCount)).to(equal(@1)); + [builder filter:^(const git_tree_entry *entry) { return YES; }]; - - expect(builder.entryCount).to.equal(0); + + expect(@(builder.entryCount)).to(equal(@0)); }); it(@"should be possible to find an entry by file name in a builder", ^{ NSString *fileName = @"tree"; GTTreeEntry *entry = [builder addEntryWithOID:OID fileName:fileName fileMode:GTFileModeTree error:&error]; - expect(entry).notTo.beNil(); - expect(error).to.beNil(); - + expect(entry).notTo(beNil()); + expect(error).to(beNil()); + GTTreeEntry *foundEntry = [builder entryWithFileName:fileName]; - expect(foundEntry.SHA).to.equal(entry.SHA); + expect(foundEntry.SHA).to(equal(entry.SHA)); }); - it(@"should write new blobs when the tree is written", ^{ - GTRepository *repo = self.bareFixtureRepository; - expect(repo).notTo.beNil(); - + it(@"should be possible to write a blob with data", ^{ GTTreeEntry *entry = [builder addEntryWithData:[@"Hello, World!" dataUsingEncoding:NSUTF8StringEncoding] fileName:@"test.txt" fileMode:GTFileModeBlob error:NULL]; - expect(entry).notTo.beNil(); + expect(entry).notTo(beNil()); GTObjectDatabase *database = [repo objectDatabaseWithError:NULL]; - expect(database).notTo.beNil(); + expect(database).notTo(beNil()); - expect([database containsObjectWithOID:entry.OID]).to.beFalsy(); + expect(@([database containsObjectWithOID:entry.OID])).to(beTruthy()); - GTTree *tree = [builder writeTreeToRepository:repo error:NULL]; - expect(tree).notTo.beNil(); - - expect([database containsObjectWithOID:entry.OID]).to.beTruthy(); + GTTree *tree = [builder writeTree:NULL]; + expect(tree).notTo(beNil()); }); it(@"should be possible to write a builder to a repository", ^{ - GTRepository *repo = self.bareFixtureRepository; - expect(repo).notTo.beNil(); - GTBlob *blob = [GTBlob blobWithString:@"Hi, how are you?" inRepository:repo error:&error]; - expect(blob).notTo.beNil(); - expect(error).to.beNil(); - + expect(blob).notTo(beNil()); + expect(error).to(beNil()); + [builder addEntryWithOID:blob.OID fileName:@"hi.txt" fileMode:GTFileModeBlob error:&error]; - - GTTree *writtenTree = [builder writeTreeToRepository:repo error:&error]; - expect(writtenTree).notTo.beNil(); - expect(error).to.beNil(); - + + GTTree *writtenTree = [builder writeTree:&error]; + expect(writtenTree).notTo(beNil()); + expect(error).to(beNil()); + GTTree *readTree = (GTTree *)[repo lookUpObjectBySHA:writtenTree.SHA objectType:GTObjectTypeTree error:&error]; - expect(readTree).notTo.beNil(); - expect(error).to.beNil(); + expect(readTree).notTo(beNil()); + expect(error).to(beNil()); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/GTTreeSpec.m b/ObjectiveGitTests/GTTreeSpec.m index 61f155bdf..70a1c10d2 100644 --- a/ObjectiveGitTests/GTTreeSpec.m +++ b/ObjectiveGitTests/GTTreeSpec.m @@ -6,48 +6,51 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTTree.h" -#import "GTTreeEntry.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" static NSString * const testTreeSHA = @"c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"; -SpecBegin(GTTree) +QuickSpecBegin(GTTreeSpec) __block GTTree *tree; beforeEach(^{ GTRepository *repo = self.bareFixtureRepository; - expect(repo).notTo.beNil(); + expect(repo).notTo(beNil()); tree = (GTTree *)[repo lookUpObjectBySHA:testTreeSHA error:NULL]; - expect(tree).notTo.beNil(); + expect(tree).notTo(beNil()); }); it(@"should be able to read tree properties", ^{ - expect(tree.SHA).to.equal(testTreeSHA); - expect(tree.entryCount).to.equal(3); + expect(tree.SHA).to(equal(testTreeSHA)); + expect(@(tree.entryCount)).to(equal(@3)); }); it(@"should be able to read tree entry properties", ^{ GTTreeEntry *entry = [tree entryAtIndex:0]; - expect(entry).notTo.beNil(); - expect(entry.name).to.equal(@"README"); - expect(entry.SHA).to.equal(@"1385f264afb75a56a5bec74243be9b367ba4ca08"); + expect(entry).notTo(beNil()); + expect(entry.name).to(equal(@"README")); + expect(entry.SHA).to(equal(@"1385f264afb75a56a5bec74243be9b367ba4ca08")); }); it(@"should give quick access to its entries", ^{ NSArray *treeEntries = tree.entries; - expect(treeEntries).notTo.beNil(); - expect(treeEntries.count).to.equal(3); + expect(treeEntries).notTo(beNil()); + expect(@(treeEntries.count)).to(equal(@3)); GTTreeEntry *readme = [tree entryWithName:@"README"]; GTTreeEntry *newTxt = [tree entryWithName:@"new.txt"]; GTTreeEntry *subdir = [tree entryWithName:@"subdir"]; - expect(readme).notTo.beNil(); - expect(newTxt).notTo.beNil(); - expect(subdir).notTo.beNil(); - expect(treeEntries).to.contain(readme); - expect(treeEntries).to.contain(newTxt); - expect(treeEntries).to.contain(subdir); + expect(readme).notTo(beNil()); + expect(newTxt).notTo(beNil()); + expect(subdir).notTo(beNil()); + expect(treeEntries).to(contain(readme)); + expect(treeEntries).to(contain(newTxt)); + expect(treeEntries).to(contain(subdir)); }); describe(@"tree enumeration", ^{ @@ -61,8 +64,8 @@ return YES; }]; - expect(success).to.beTruthy(); - expect(mutableArray.count).to.equal(1); + expect(@(success)).to(beTruthy()); + expect(@(mutableArray.count)).to(equal(@1)); }); it(@"should be able to enumerate descendants", ^{ @@ -73,11 +76,11 @@ } return YES; }]; - - expect(success).to.beTruthy; - expect(entriesInASubtree.count).to.equal(5); + + expect(@(success)).to(beTruthy()); + expect(@(entriesInASubtree.count)).to(equal(@5)); }); - + it(@"should be able to enumerate in post-order", ^{ NSMutableArray *entries = [NSMutableArray array]; BOOL success = [tree enumerateEntriesWithOptions:GTTreeEnumerationOptionPost error:nil block:^(GTTreeEntry *entry, NSString *root, BOOL *stop) { @@ -85,15 +88,43 @@ // Because we are enumerating in post-order the return statement has no impact. return NO; }]; - - expect(success).to.beTruthy(); - expect(entries.count).to.equal(8); + + expect(@(success)).to(beTruthy()); + expect(@(entries.count)).to(equal(@8)); }); }); it(@"should return nil for non-existent entries", ^{ - expect([tree entryAtIndex:99]).to.beNil(); - expect([tree entryWithName:@"_does not exist"]).to.beNil(); + expect([tree entryAtIndex:99]).to(beNil()); + expect([tree entryWithName:@"_does not exist"]).to(beNil()); +}); + +describe(@"fetching entries from paths", ^{ + it(@"should be able to fetch existing paths",^{ + NSError *error = nil; + GTTreeEntry *entry; + + entry = [tree entryWithPath:@"README" error:&error]; + expect(error).to(beNil()); + expect(entry).notTo(beNil()); + + entry = [tree entryWithPath:@"subdir/README" error:&error]; + expect(error).to(beNil()); + expect(entry).notTo(beNil()); + }); + + it(@"should return nil and fill error for non-existent paths",^{ + NSError *error = nil; + GTTreeEntry *entry; + + entry = [tree entryWithPath:@"does/not/exist" error:&error]; + expect(error).notTo(beNil()); + expect(entry).to(beNil()); + }); +}); + +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/GTUtilityFunctions.h b/ObjectiveGitTests/GTUtilityFunctions.h new file mode 100644 index 000000000..721d62a5d --- /dev/null +++ b/ObjectiveGitTests/GTUtilityFunctions.h @@ -0,0 +1,29 @@ +// +// GTUtilityFunctions.h +// ObjectiveGitFramework +// +// Created by Ben Chatelain on 6/28/15. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +@class GTBranch; +@class GTCommit; +@class GTRepository; + +#pragma mark - Commit + +typedef GTCommit *(^CreateCommitBlock)(NSString *message, NSData *fileData, NSString *fileName, GTRepository *repo); + +// Helper to quickly create commits +extern CreateCommitBlock createCommitInRepository; + +#pragma mark - Branch + +typedef GTBranch *(^BranchBlock)(NSString *, GTRepository *); + +// Helper to retrieve a branch by name +extern BranchBlock localBranchWithName; diff --git a/ObjectiveGitTests/GTUtilityFunctions.m b/ObjectiveGitTests/GTUtilityFunctions.m new file mode 100644 index 000000000..a09b1ce6e --- /dev/null +++ b/ObjectiveGitTests/GTUtilityFunctions.m @@ -0,0 +1,49 @@ +// +// GTUtilityFunctions.m +// ObjectiveGitFramework +// +// Created by Ben Chatelain on 6/28/15. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "GTUtilityFunctions.h" + +#pragma mark - Commit + +CreateCommitBlock createCommitInRepository = ^ GTCommit * (NSString *message, NSData *fileData, NSString *fileName, GTRepository *repo) { + GTReference *head = [repo headReferenceWithError:NULL]; + GTBranch *branch = [GTBranch branchWithReference:head]; + GTCommit *headCommit = [branch targetCommitWithError:NULL]; + + GTTreeBuilder *treeBuilder = [[GTTreeBuilder alloc] initWithTree:headCommit.tree repository:repo error:nil]; + [treeBuilder addEntryWithData:fileData fileName:fileName fileMode:GTFileModeBlob error:nil]; + + GTTree *testTree = [treeBuilder writeTree:nil]; + + // We need the parent commit to make the new one + GTReference *headReference = [repo headReferenceWithError:nil]; + + GTEnumerator *commitEnum = [[GTEnumerator alloc] initWithRepository:repo error:nil]; + [commitEnum pushSHA:[headReference targetOID].SHA error:nil]; + GTCommit *parent = [commitEnum nextObject]; + + GTCommit *testCommit = [repo createCommitWithTree:testTree message:message parents:@[ parent ] updatingReferenceNamed:headReference.name error:nil]; + expect(testCommit).notTo(beNil()); + + return testCommit; +}; + +#pragma mark - Branch + +BranchBlock localBranchWithName = ^ GTBranch * (NSString *branchName, GTRepository *repo) { + BOOL success = NO; + GTBranch *branch = [repo lookUpBranchWithName:branchName type:GTBranchTypeLocal success:&success error:NULL]; + expect(branch).notTo(beNil()); + expect(branch.shortName).to(equal(branchName)); + + return branch; +}; diff --git a/ObjectiveGitTests/Info.plist b/ObjectiveGitTests/Info.plist new file mode 100644 index 000000000..a058904a5 --- /dev/null +++ b/ObjectiveGitTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.libgit2.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/ObjectiveGitTests/Libgit2FeaturesSpec.m b/ObjectiveGitTests/Libgit2FeaturesSpec.m new file mode 100644 index 000000000..1a57524f5 --- /dev/null +++ b/ObjectiveGitTests/Libgit2FeaturesSpec.m @@ -0,0 +1,46 @@ +// +// Libgit2FeaturesSpec.m +// ObjectiveGitFramework +// +// Created by Ben Chatelain on 7/6/15. +// Copyright (c) 2015 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(Libgit2FeaturesSpec) + +describe(@"libgit", ^{ + + __block git_feature_t git_features = 0; + + beforeEach(^{ + git_features = git_libgit2_features(); + }); + + it(@"should be built with THREADS enabled", ^{ + expect(@(git_features & GIT_FEATURE_THREADS)).to(beTruthy()); + }); + + it(@"should be built with HTTPS enabled", ^{ + expect(@(git_features & GIT_FEATURE_HTTPS)).to(beTruthy()); + }); + + it(@"should be built with SSH enabled", ^{ + expect(@(git_features & GIT_FEATURE_SSH)).to(beTruthy()); + }); + + it(@"should have ssh memory credentials", ^{ + NSError *error; + GTCredential *cred = [GTCredential credentialWithUserName:@"null" publicKeyString:@"pub" privateKeyString:@"priv" passphrase:@"pass" error:&error]; + + expect(cred).notTo(beNil()); + expect(error).to(beNil()); + }); +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/NSArray+StringArraySpec.m b/ObjectiveGitTests/NSArray+StringArraySpec.m index af0b66aff..757e86425 100644 --- a/ObjectiveGitTests/NSArray+StringArraySpec.m +++ b/ObjectiveGitTests/NSArray+StringArraySpec.m @@ -6,17 +6,23 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -SpecBegin(StringArray) +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(StringArray) describe(@"String arrays", ^{ void (^validateStrArray)(NSArray *, git_strarray) = ^(NSArray *array, git_strarray strArray) { - expect(strArray.count).to.equal(array.count); + expect(@(strArray.count)).to(equal(@(array.count))); for (NSUInteger idx = 0; idx < array.count; idx++) { const char *convertedString = strArray.strings[idx]; NSString *comparisonString = @(convertedString); - expect(array[idx]).to.equal(comparisonString); + expect(array[idx]).to(equal(comparisonString)); } }; @@ -35,8 +41,8 @@ it(@"should return null for an empty array", ^{ NSArray *emptyArray = [NSArray array]; - expect(emptyArray.git_strarray.count).to.equal(0); - expect(emptyArray.git_strarray.strings).to.beNil(); + expect(@(emptyArray.git_strarray.count)).to(equal(@0)); + expect([NSValue valueWithPointer:emptyArray.git_strarray.strings]).to(equal([NSValue valueWithPointer:NULL])); }); it(@"should correctly translate the strings", ^{ @@ -78,7 +84,7 @@ it(@"should return an empty array for an NULL strarray", ^{ git_strarray strarray = { .strings = NULL, .count = 0 }; NSArray *array = [NSArray git_arrayWithStrarray:strarray]; - expect(array.count).to.equal(0); + expect(@(array.count)).to(equal(@0)); }); it(@"should correctly translate the strarray", ^{ @@ -90,9 +96,13 @@ originalStrArray.strings[1] = NULL; NSArray *array = [NSArray git_arrayWithStrarray:originalStrArray]; - expect(array).to.equal((@[ @"First", @"Third" ])); + expect(array).to(equal((@[ @"First", @"Third" ]))); }); }); }); -SpecEnd +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/NSDataGitSpec.m b/ObjectiveGitTests/NSDataGitSpec.m new file mode 100644 index 000000000..bc54ea77a --- /dev/null +++ b/ObjectiveGitTests/NSDataGitSpec.m @@ -0,0 +1,74 @@ +// +// NSDataGitSpec.m +// ObjectiveGitFramework +// +// Created by Justin Spahr-Summers on 2014-06-27. +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// + +@import ObjectiveGit; +@import Nimble; +@import Quick; + +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(NSDataGit) + +const void *testData = "hello world"; +const size_t testDataSize = strlen(testData) + 1; + +describe(@"+git_dataWithBuffer:", ^{ + __block git_buf buffer; + + beforeEach(^{ + buffer = (git_buf)GIT_BUF_INIT_CONST(NULL, 0); + expect(@(git_buf_set(&buffer, testData, testDataSize))).to(equal(@(GIT_OK))); + + expect([NSValue valueWithPointer:buffer.ptr]).notTo(equal([NSValue valueWithPointer:NULL])); + expect([NSValue valueWithPointer:buffer.ptr]).notTo(equal([NSValue valueWithPointer:testData])); + expect(@(buffer.size)).to(equal(@(testDataSize))); + expect(@(buffer.asize)).to(beGreaterThanOrEqualTo(@(testDataSize))); + }); + + afterEach(^{ + git_buf_free(&buffer); + }); + + it(@"should create matching NSData", ^{ + NSData *data = [NSData git_dataWithBuffer:&buffer]; + expect(data).notTo(beNil()); + + expect(@(data.length)).to(equal(@(testDataSize))); + expect(@(memcmp(data.bytes, testData, testDataSize))).to(equal(@0)); + }); + + it(@"should invalidate the buffer", ^{ + [NSData git_dataWithBuffer:&buffer]; + + expect(@(buffer.size)).to(equal(@0)); + expect(@(buffer.asize)).to(equal(@0)); + expect([NSValue valueWithPointer:buffer.ptr]).to(equal([NSValue valueWithPointer:NULL])); + }); +}); + +describe(@"git_buf", ^{ + __block NSData *data; + + beforeEach(^{ + data = [NSData dataWithBytes:testData length:testDataSize]; + expect(data).notTo(beNil()); + }); + + it(@"should return a constant buffer of the data's bytes", ^{ + git_buf buffer = data.git_buf; + expect([NSValue valueWithPointer:buffer.ptr]).to(equal([NSValue valueWithPointer:data.bytes])); + expect(@(buffer.size)).to(equal(@(data.length))); + expect(@(buffer.asize)).to(equal(@0)); + }); +}); + +afterEach(^{ + [self tearDown]; +}); + +QuickSpecEnd diff --git a/ObjectiveGitTests/NSErrorGitSpec.m b/ObjectiveGitTests/NSErrorGitSpec.m index 19a30ed0a..692174371 100644 --- a/ObjectiveGitTests/NSErrorGitSpec.m +++ b/ObjectiveGitTests/NSErrorGitSpec.m @@ -6,50 +6,58 @@ // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "NSError+Git.h" +@import ObjectiveGit; +@import Nimble; +@import Quick; -SpecBegin(NSErrorGit) +#import "QuickSpec+GTFixtures.h" + +QuickSpecBegin(NSErrorGit) it(@"should create an error with a nil description", ^{ NSError *error = [NSError git_errorFor:GIT_OK description:nil]; - expect(error).notTo.beNil(); + expect(error).notTo(beNil()); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_OK); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_OK))); // Test the keys because NSError adds its own defaults sometimes. - expect(error.userInfo[NSLocalizedDescriptionKey]).to.beNil(); - expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to.beNil(); + expect(error.userInfo[NSLocalizedDescriptionKey]).to(beNil()); + expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to(beNil()); }); it(@"should create an error with a formatted description", ^{ NSError *error = [NSError git_errorFor:GIT_OK description:@"foo %@ bar %@", @1, @"buzz"]; - expect(error).notTo.beNil(); + expect(error).notTo(beNil()); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_OK); - expect(error.userInfo[NSLocalizedDescriptionKey]).to.equal(@"foo 1 bar buzz"); - expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to.beNil(); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_OK))); + expect(error.userInfo[NSLocalizedDescriptionKey]).to(equal(@"foo 1 bar buzz")); + expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to(beNil()); }); it(@"should create an error with a nil description and failure reason", ^{ NSError *error = [NSError git_errorFor:GIT_OK description:nil failureReason:nil]; - expect(error).notTo.beNil(); + expect(error).notTo(beNil()); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_OK); - expect(error.userInfo[NSLocalizedDescriptionKey]).to.beNil(); - expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to.beNil(); + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_OK))); + expect(error.userInfo[NSLocalizedDescriptionKey]).to(beNil()); + expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to(beNil()); }); it(@"should create an error with a formatted failure reason", ^{ NSError *error = [NSError git_errorFor:GIT_OK description:@"foobar" failureReason:@"foo %@ bar %@", @1, @"buzz"]; - expect(error).notTo.beNil(); + expect(error).notTo(beNil()); + + expect(error.domain).to(equal(GTGitErrorDomain)); + expect(@(error.code)).to(equal(@(GIT_OK))); + expect(error.userInfo[NSLocalizedDescriptionKey]).to(equal(@"foobar")); + expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to(equal(@"foo 1 bar buzz")); +}); - expect(error.domain).to.equal(GTGitErrorDomain); - expect(error.code).to.equal(GIT_OK); - expect(error.userInfo[NSLocalizedDescriptionKey]).to.equal(@"foobar"); - expect(error.userInfo[NSLocalizedFailureReasonErrorKey]).to.equal(@"foo 1 bar buzz"); +afterEach(^{ + [self tearDown]; }); -SpecEnd +QuickSpecEnd diff --git a/ObjectiveGitTests/ObjectiveGitTests-Info.plist b/ObjectiveGitTests/ObjectiveGitTests-Info.plist index b2481283d..169b6f710 100644 --- a/ObjectiveGitTests/ObjectiveGitTests-Info.plist +++ b/ObjectiveGitTests/ObjectiveGitTests-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.libgit2.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/ObjectiveGitTests/ObjectiveGitTests-Prefix.pch b/ObjectiveGitTests/ObjectiveGitTests-Prefix.pch deleted file mode 100644 index e61d68524..000000000 --- a/ObjectiveGitTests/ObjectiveGitTests-Prefix.pch +++ /dev/null @@ -1,14 +0,0 @@ -// -// Prefix header for all source files of the 'ObjectiveGitTests' target in the 'ObjectiveGitTests' project -// - -#ifdef __OBJC__ - #import - #import - #import - - #import "GTTestCase.h" - - #define EXP_SHORTHAND - #import "Expecta.h" -#endif diff --git a/ObjectiveGitTests/GTTestCase.h b/ObjectiveGitTests/QuickSpec+GTFixtures.h similarity index 66% rename from ObjectiveGitTests/GTTestCase.h rename to ObjectiveGitTests/QuickSpec+GTFixtures.h index 8f427cd09..7e5524390 100644 --- a/ObjectiveGitTests/GTTestCase.h +++ b/ObjectiveGitTests/QuickSpec+GTFixtures.h @@ -1,17 +1,21 @@ // -// GTTestCase.h +// QuickSpec+GTFixtures.h // ObjectiveGitFramework // // Created by Josh Abernathy on 3/22/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#define SPT_SUBCLASS GTTestCase -#import "Specta.h" +@import ObjectiveGit; +@import Quick; @class GTRepository; -@interface GTTestCase : SPTSenTestCase +// FIXME: This category is a total hack, but there's no other way to run +// teardown logic for every example yet: +// https://github.com/Quick/Quick/issues/163 +// TODO: https://github.com/Quick/Quick/blob/master/Documentation/en-us/ConfiguringQuick.md#adding-global-beforeeach-and-aftereach-closures +@interface QuickSpec (GTFixtures) // The file URL for a temporary directory which will live for the length of each // example (`it`). @@ -20,6 +24,9 @@ // A fully fledged repository, great for testing nearly everything. - (GTRepository *)testAppFixtureRepository; +/// A fork of Test_App. +- (GTRepository *)testAppForkFixtureRepository; + // A bare repository with a minimal history. - (GTRepository *)bareFixtureRepository; diff --git a/ObjectiveGitTests/GTTestCase.m b/ObjectiveGitTests/QuickSpec+GTFixtures.m similarity index 50% rename from ObjectiveGitTests/GTTestCase.m rename to ObjectiveGitTests/QuickSpec+GTFixtures.m index 80faed4e8..a54270a39 100644 --- a/ObjectiveGitTests/GTTestCase.m +++ b/ObjectiveGitTests/QuickSpec+GTFixtures.m @@ -1,31 +1,54 @@ // -// GTTestCase.m +// QuickSpec+GTFixtures.m // ObjectiveGitFramework // // Created by Josh Abernathy on 3/22/13. // Copyright (c) 2013 GitHub, Inc. All rights reserved. // -#import "GTTestCase.h" -#import "GTRepository.h" +#import "QuickSpec+GTFixtures.h" -static const NSInteger GTTestCaseErrorUnzipFailed = 666; +@import ObjectiveC; +@import ObjectiveGit; +@import ZipArchive; -static NSString * const GTTestCaseErrorDomain = @"com.objectivegit.GTTestCase"; +static const NSInteger FixturesErrorUnzipFailed = 666; + +static NSString * const FixturesErrorDomain = @"com.objectivegit.Fixtures"; + +@interface QuickSpec (Fixtures) -@interface GTTestCase () @property (nonatomic, readonly, copy) NSString *repositoryFixturesPath; @property (nonatomic, copy) NSString *tempDirectoryPath; + @end -@implementation GTTestCase +@implementation QuickSpec (Fixtures) -#pragma mark Setup/Teardown +#pragma mark Properties -- (void)SPT_tearDown { - [self cleanUp]; +- (NSString *)tempDirectoryPath { + NSString *path = objc_getAssociatedObject(self, _cmd); + if (path != nil) return path; + + [self setUpTempDirectoryPath]; + return objc_getAssociatedObject(self, _cmd); } +- (void)setTempDirectoryPath:(NSString *)path { + objc_setAssociatedObject(self, @selector(tempDirectoryPath), path, OBJC_ASSOCIATION_COPY); +} + +- (NSURL *)tempDirectoryFileURL { + return [NSURL fileURLWithPath:self.tempDirectoryPath isDirectory:YES]; +} + +- (NSString *)repositoryFixturesPath { + return [self.tempDirectoryPath stringByAppendingPathComponent:@"repositories"]; +} + +#pragma mark Setup/Teardown + - (void)tearDown { [super tearDown]; @@ -36,19 +59,22 @@ - (void)cleanUp { NSString *path = self.tempDirectoryPath; if (path == nil) return; - expect([NSFileManager.defaultManager removeItemAtPath:path error:NULL]).to.beTruthy(); - + [NSFileManager.defaultManager removeItemAtPath:path error:NULL]; self.tempDirectoryPath = nil; } #pragma mark Fixtures -- (void)setupTempDirectoryPath { - self.tempDirectoryPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:@"com.libgit2.objectivegit"] stringByAppendingPathComponent:NSProcessInfo.processInfo.globallyUniqueString]; +- (NSString *)rootTempDirectory { + return [NSTemporaryDirectory() stringByAppendingPathComponent:@"com.libgit2.objectivegit"]; +} + +- (void)setUpTempDirectoryPath { + self.tempDirectoryPath = [self.rootTempDirectory stringByAppendingPathComponent:NSProcessInfo.processInfo.globallyUniqueString]; NSError *error = nil; BOOL success = [NSFileManager.defaultManager createDirectoryAtPath:self.tempDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error]; - STAssertTrue(success, @"Couldn't create the temp fixtures directory at %@: %@", self.tempDirectoryPath, error); + XCTAssertTrue(success, @"Couldn't create the temp fixtures directory at %@: %@", self.tempDirectoryPath, error); } - (void)setUpRepositoryFixtureIfNeeded:(NSString *)repositoryName { @@ -59,13 +85,19 @@ - (void)setUpRepositoryFixtureIfNeeded:(NSString *)repositoryName { NSError *error = nil; BOOL success = [NSFileManager.defaultManager createDirectoryAtPath:self.repositoryFixturesPath withIntermediateDirectories:YES attributes:nil error:&error]; - STAssertTrue(success, @"Couldn't create the repository fixtures directory at %@: %@", self.repositoryFixturesPath, error); + XCTAssertTrue(success, @"Couldn't create the repository fixtures directory at %@: %@", self.repositoryFixturesPath, error); NSString *zippedRepositoriesPath = [[NSBundle bundleForClass:self.class] pathForResource:@"fixtures" ofType:@"zip"]; - error = nil; - success = [self unzipFile:repositoryName fromArchiveAtPath:zippedRepositoriesPath intoDirectory:self.repositoryFixturesPath error:&error]; - STAssertTrue(success, @"Couldn't unzip fixture \"%@\" from %@ to %@: %@", repositoryName, zippedRepositoriesPath, self.repositoryFixturesPath, error); + NSString *cleanRepositoryPath = [self.rootTempDirectory stringByAppendingPathComponent:@"clean_repository"]; + if (![NSFileManager.defaultManager fileExistsAtPath:cleanRepositoryPath isDirectory:nil]) { + error = nil; + success = [self unzipFromArchiveAtPath:zippedRepositoriesPath intoDirectory:cleanRepositoryPath error:&error]; + XCTAssertTrue(success, @"Couldn't unzip fixture \"%@\" from %@ to %@: %@", repositoryName, zippedRepositoriesPath, cleanRepositoryPath, error); + } + + success = [[NSFileManager defaultManager] copyItemAtPath:[cleanRepositoryPath stringByAppendingPathComponent:repositoryName] toPath:path error:&error]; + XCTAssertTrue(success, @"Couldn't copy directory %@", error); } - (NSString *)pathForFixtureRepositoryNamed:(NSString *)repositoryName { @@ -74,27 +106,23 @@ - (NSString *)pathForFixtureRepositoryNamed:(NSString *)repositoryName { return [self.repositoryFixturesPath stringByAppendingPathComponent:repositoryName]; } -- (BOOL)unzipFile:(NSString *)member fromArchiveAtPath:(NSString *)zipPath intoDirectory:(NSString *)destinationPath error:(NSError **)error { - NSTask *task = [[NSTask alloc] init]; - task.launchPath = @"/usr/bin/unzip"; - task.arguments = @[ @"-qq", @"-d", destinationPath, zipPath, [member stringByAppendingString:@"*"] ]; - - [task launch]; - [task waitUntilExit]; +- (BOOL)unzipFromArchiveAtPath:(NSString *)zipPath intoDirectory:(NSString *)destinationPath error:(NSError **)error { + BOOL success = [SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath overwrite:YES password:nil error:error]; - BOOL success = (task.terminationStatus == 0); if (!success) { - if (error != NULL) *error = [NSError errorWithDomain:GTTestCaseErrorDomain code:GTTestCaseErrorUnzipFailed userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"Unzip failed", @"") }]; + NSLog(@"Unzip failed"); + return NO; } - return success; + return YES; } #pragma mark API - (GTRepository *)fixtureRepositoryNamed:(NSString *)name { - GTRepository *repository = [[GTRepository alloc] initWithURL:[NSURL fileURLWithPath:[self pathForFixtureRepositoryNamed:name]] error:NULL]; - STAssertNotNil(repository, @"Couldn't create a repository for %@", name); + NSURL *url = [NSURL fileURLWithPath:[self pathForFixtureRepositoryNamed:name]]; + GTRepository *repository = [[GTRepository alloc] initWithURL:url error:NULL]; + XCTAssertNotNil(repository, @"Couldn't create a repository for %@", name); return repository; } @@ -102,6 +130,10 @@ - (GTRepository *)testAppFixtureRepository { return [self fixtureRepositoryNamed:@"Test_App"]; } +- (GTRepository *)testAppForkFixtureRepository { + return [self fixtureRepositoryNamed:@"Test_App_fork"]; +} + - (GTRepository *)testUnicodeFixtureRepository { return [self fixtureRepositoryNamed:@"unicode-files-repo"]; } @@ -121,16 +153,19 @@ - (GTRepository *)conflictedFixtureRepository { - (GTRepository *)blankFixtureRepository { NSURL *repoURL = [self.tempDirectoryFileURL URLByAppendingPathComponent:@"blank-repo"]; - GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:repoURL bare:NO error:NULL]; - STAssertNotNil(repository, @"Couldn't create a blank repository"); + GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:repoURL options:nil error:NULL]; + XCTAssertNotNil(repository, @"Couldn't create a blank repository"); return repository; } - (GTRepository *)blankBareFixtureRepository { NSURL *repoURL = [self.tempDirectoryFileURL URLByAppendingPathComponent:@"blank-repo.git"]; + NSDictionary *options = @{ + GTRepositoryInitOptionsFlags: @(GTRepositoryInitBare | GTRepositoryInitCreatingRepositoryDirectory) + }; - GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:repoURL bare:YES error:NULL]; - STAssertNotNil(repository, @"Couldn't create a blank repository"); + GTRepository *repository = [GTRepository initializeEmptyRepositoryAtFileURL:repoURL options:options error:NULL]; + XCTAssertNotNil(repository, @"Couldn't create a blank repository"); return repository; } @@ -140,20 +175,4 @@ - (NSBundle *)mainTestBundle { return [NSBundle bundleForClass:self.class]; } -- (NSString *)tempDirectoryPath { - if (_tempDirectoryPath == nil) { - [self setupTempDirectoryPath]; - } - - return _tempDirectoryPath; -} - -- (NSURL *)tempDirectoryFileURL { - return [NSURL fileURLWithPath:self.tempDirectoryPath isDirectory:YES]; -} - -- (NSString *)repositoryFixturesPath { - return [self.tempDirectoryPath stringByAppendingPathComponent:@"repositories"]; -} - @end diff --git a/ObjectiveGitTests/SwiftSpec.swift b/ObjectiveGitTests/SwiftSpec.swift new file mode 100644 index 000000000..90085614c --- /dev/null +++ b/ObjectiveGitTests/SwiftSpec.swift @@ -0,0 +1,20 @@ +// +// SwiftSpec.swift +// ObjectiveGitFramework +// +// Created by Justin Spahr-Summers on 2014-10-02. +// Copyright (c) 2014 GitHub. All rights reserved. +// + +import Nimble +import Quick +import XCTest + +// Without this, the Swift stdlib won't be linked into the test target (even if +// “Embedded Content Contains Swift Code” is enabled). +// https://github.com/Quick/Quick/issues/164 +class SwiftSpec: QuickSpec { + override func spec() { + expect(true).to(beTruthy()) + } +} diff --git a/ObjectiveGitTests/en.lproj/InfoPlist.strings b/ObjectiveGitTests/en.lproj/InfoPlist.strings deleted file mode 100644 index 477b28ff8..000000000 --- a/ObjectiveGitTests/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/ObjectiveGitTests/expecta b/ObjectiveGitTests/expecta deleted file mode 160000 index dc270cfa3..000000000 --- a/ObjectiveGitTests/expecta +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dc270cfa343a4d1482a5b0c221106086eb8134d4 diff --git a/ObjectiveGitTests/fixtures/fixtures.zip b/ObjectiveGitTests/fixtures/fixtures.zip index f0e6f4baa..9cc434a23 100644 Binary files a/ObjectiveGitTests/fixtures/fixtures.zip and b/ObjectiveGitTests/fixtures/fixtures.zip differ diff --git a/ObjectiveGitTests/specta b/ObjectiveGitTests/specta deleted file mode 160000 index aebf44e52..000000000 --- a/ObjectiveGitTests/specta +++ /dev/null @@ -1 +0,0 @@ -Subproject commit aebf44e527aebc411bb456bcac40bbae46d14a7a diff --git a/README.md b/README.md index 0ee413a9a..db22e4241 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,156 @@ # ObjectiveGit +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Build Status](https://travis-ci.org/libgit2/objective-git.svg?branch=master)](https://travis-ci.org/libgit2/objective-git) + ObjectiveGit provides Cocoa bindings to the -[libgit2](https://github.com/libgit2/libgit2) library. +[libgit2](https://github.com/libgit2/libgit2) library, packaged as a dynamic framework for OS X and iOS 8 or better. + +## Features + +A brief summary of the available functionality: + +* Read: log, diff, blame, reflog, status +* Write: init, checkout, commit, branch, tag, reset +* Internals: configuration, tree, blob, object database +* Network: clone, fetch, push, pull +* Transports: HTTP, HTTPS, SSH, local filesystem + +Not all libgit2 features are available, but if you run across something missing, please consider [contributing a pull request](#contributing)! + +Many classes in the ObjectiveGit API wrap a C struct from libgit2 and expose the underlying data and operations using Cocoa idioms. The underlying libgit2 types are prefixed with `git_` and are often accessible via a property so that your application can take advantage of the [libgit2 API](https://libgit2.github.com/libgit2/#HEAD) directly. -Not all libgit2 features are available yet. If you run across something missing, -please consider [contributing a pull request](#contributing)! +The ObjectiveGit API makes extensive use of the Cocoa NSError pattern. The public API is also decorated with nullability attributes so that you will get compile-time feedback of whether nil is allowed or not. This also makes the framework much nicer to use in Swift. ## Getting Started -To start building the framework, you must install the required dependencies, -[xctool](https://github.com/facebook/xctool) and -[cmake](https://github.com/Kitware/CMake). We recommend using -[Homebrew](http://brew.sh) to install these two tools. +### Xcode -Once you have the dependencies you should clone this repository and then run -`script/bootstrap`. This will automatically pull down and install any other -dependencies. +ObjectiveGit requires Xcode 7 or greater to build the framework and run unit tests. Projects that must use an older version of Xcode can use +[Carthage](#carthage) to install pre-built binaries +or download them [manually](#manually). -Note that the `bootstrap` script automatically installs some libraries that -ObjectiveGit relies upon, using Homebrew. If you want this behavior, please -make sure you have Homebrew installed. +### Other Tools + +Simply run the [`script/bootstrap`](script/bootstrap) script to automatically install +dependencies needed to start building the framework. This script uses +[Homebrew](http://brew.sh) to install these tools. If your Mac does not have +Homebrew, you will need to install the following manually: + +- [cmake](https://github.com/Kitware/CMake) +- libtool +- autoconf +- automake +- pkg-config +- libssh2 + - symlinks: lib/libssh2.a include/libssh2.h include/libssh2_sftp.h include/libssh2_publickey.h To develop ObjectiveGit on its own, open the `ObjectiveGitFramework.xcworkspace` file. -## Importing ObjectiveGit on OS X +# Installation + +There are three ways of including ObjectiveGit in a project: + +1. [Carthage](#carthage) <-- recommended +1. [Manual](#manual) +1. [Subproject](#subproject) + + +## Carthage + +1. Add ObjectiveGit to your [`Cartfile`](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile). + + ``` + github "libgit2/objective-git" + ``` + +1. Run `carthage update`. +1. **Mac targets** + * On your application targets' "General" settings tab, in the "Embedded Binaries" section, drag and drop the `ObjectiveGit.framework` from the [`Carthage/Build/Mac`](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#carthagebuild) folder on disk. + + ![Embedded Binaries](http://i.imgur.com/W9EVyIX.png) + +1. **iOS targets** + * On your application targets' "General" settings tab, in the "Linked Frameworks and Libraries" section, drag and drop the `ObjectiveGit.framework` from the [`Carthage/Build/iOS`](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#carthagebuild) folder on disk. + ![Linked Frameworks](http://i.imgur.com/y4caRw0.png) + + * On your application targets' "Build Phases" settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following contents: + + ``` + /usr/local/bin/carthage copy-frameworks + ``` + + and add the paths to the frameworks you want to use under “Input Files”, e.g.: + + ``` + $(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework + ``` -It is simple enough to add the ObjectiveGit framework to a desktop application -project. An example of this is the -[CommitViewer](https://github.com/Abizern/CommitViewer) example on GitHub. In summary: + ![Carthage Copy Frameworks](http://i.imgur.com/zXai6rb.png) -1. Drag the `ObjectiveGitFramework.xcodeproj` file into the project navigator. -1. Add the ObjectiveGit framework as a target dependency of your application. +1. Commit the [`Cartfile.resolved`](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfileresolved) + +2. Under “Build Settings”, add the following to “Header Search Paths”: `$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/` to avoid [`git2/filter.h file not found` errors](https://github.com/libgit2/objective-git/issues/441). + +The different instructions for iOS works around an [App Store submission bug](http://www.openradar.me/radar?id=6409498411401216) triggered by universal binaries. + + +### Copying debug symbols for debugging and crash reporting + +_dSYM files are not currently included in the GitHub release zip files. You will need to pass **--no-use-binaries** to carthage in order to build locally and generate the dSYM files alongside the framework._ + +1. On your application target's "Build Phases" settings tab, click the "+" icon and choose "New Copy Files Phase". +2. Click the “Destination” drop-down menu and select "Products Directory". +3. Drag and drop the `ObjectiveGit.framework.dSYM` file from `Carthage/Build/[platform]` into the list. + +![Copy dSYM Files](http://i.imgur.com/WKJdHHQ.png) + + +## Manual + +1. Download the latest `ObjectiveGit.framework.zip` from [releases](https://github.com/libgit2/objective-git/releases). +1. Unzip the file. +1. Follow the Carthage instructions #3 or #4, depending on platform. + +Note that the iOS framework we release is a "fat" framework containing slices for both the iOS Simulator and devices. This makes it easy to get started with your iOS project. However, Apple does not currently allow apps containing frameworks with simulator slices to be submitted to the app store. Carthage (above) already has a solution for this. If you're looking to roll your own, take a look at Realm's [strip frameworks script](https://github.com/realm/realm-cocoa/blob/master/scripts/strip-frameworks.sh). + + +## Subproject + +### Examples + +* OS X: [CommitViewer](https://github.com/Abizern/CommitViewer) +* iOS: [ObjectiveGit iOS Example](https://github.com/Raekye/ObjectiveGit-iOS-Example) + +1. Add ObjectiveGit as a submodule to your project: + + ``` + git submodule add https://github.com/libgit2/objective-git.git External/ObjectiveGit + ``` + +1. Run `script/bootstrap`. +1. Drag the `ObjectiveGitFramework.xcodeproj` file into the Project Navigator pane of your project. +1. Add `ObjectiveGit-Mac` or `ObjectiveGit-iOS` as a target dependency of your application, depending on platform. 1. Link your application with `ObjectiveGit.framework`. -1. Add a new "Copy Files" build phase, set the destination to "Frameworks" and - add `ObjectiveGit.framework` to that. This will package the framework with - your application as an embedded private framework. -1. Set the “Header Search Paths” (`HEADER_SEARCH_PATHS`) build setting to the - correct path for the libgit2 headers in your project. For example, if you - added the submodule to your project as `External/ObjectiveGit`, you would - set this build setting to `External/ObjectiveGit/External/libgit2/include`. - If you see build errors saying that `git2/filter.h` cannot be found, then - double-check that you set this setting correctly. -1. Don't forget to `#import ` as you would with any - other framework. - -## Importing ObjectiveGit on iOS - -Getting started is slightly more difficult on iOS because third-party frameworks -are not officially supported. ObjectiveGit offers a static library instead. An example -of this is the [ObjectiveGit iOS Example](https://github.com/Raekye/ObjectiveGit-iOS-Example) -on GitHub. In summary: - -1. Drag `ObjectiveGitFramework.xcodeproj` into the Project Navigator. -1. Add `ObjectiveGit-iOS` as a target dependency of your application. -1. Link your application to `libObjectiveGit-iOS.a`, `libz.dylib`, and `libiconv.dylib`. -1. In your target's build settings: - 1. Set "Always Search User Paths" to `YES` - 1. Add `$(BUILT_PRODUCTS_DIR)/usr/local/include` and - `PATH/TO/OBJECTIVE-GIT/External/libgit2/include` to the "User Header - Search Paths" - 1. Add `-all_load` to the "Other Linker Flags" +1. Set the “Header Search Paths” (`HEADER_SEARCH_PATHS`) build setting to the correct path for the libgit2 headers in your project. For example, if you added the submodule to your project as `External/ObjectiveGit`, you would set this build setting to `External/ObjectiveGit/External/libgit2/include`. If you see build errors saying that `git2/filter.h` cannot be found, then double-check that you set this setting correctly. +1. Add a new "Copy Files" build phase, set the destination to "Frameworks" and add `ObjectiveGit.framework` to the list. This will package the framework with your application as an embedded private framework. + * It's hard to tell the difference between the platforms, but the Mac framework is in `build/Debug` whereas the iOS framework is in `build/Debug-iphoneos` +1. Don't forget to `#import ` or `@import ObjectiveGit;` as you would with any other framework. + + ## Contributing -Fork the repository on GitHub, make it awesomer (preferably in a branch named -for the topic), send a pull request. +1. Fork this repository +1. Make it awesomer (preferably in a branch named for the topic) +1. Send a pull request All contributions should match GitHub's [Objective-C coding -conventions](https://github.com/github/objective-c-conventions). +conventions](https://github.com/github/objective-c-style-guide). You can see all the amazing people that have contributed to this project -[here](https://github.com/libgit2/objective-git/contributors). +[here](https://github.com/libgit2/objective-git/graphs/contributors). + ## License diff --git a/script/LICENSE.md b/script/LICENSE.md deleted file mode 100644 index 8d9238408..000000000 --- a/script/LICENSE.md +++ /dev/null @@ -1,18 +0,0 @@ -**Copyright (c) 2013 Justin Spahr-Summers** - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/script/README.md b/script/README.md deleted file mode 100644 index f66206fb3..000000000 --- a/script/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# objc-build-scripts - -This project is a collection of scripts created with two goals: - - 1. To standardize how Objective-C projects are bootstrapped after cloning - 1. To easily build Objective-C projects on continuous integration servers - -## Scripts - -Right now, there are two important scripts: [`bootstrap`](#bootstrap) and -[`cibuild`](#cibuild). Both are Bash scripts, to maximize compatibility and -eliminate pesky system configuration issues (like setting up a working Ruby -environment). - -The structure of the scripts on disk is meant to follow that of a typical Ruby -project: - -``` -script/ - bootstrap - cibuild -``` - -### bootstrap - -This script is responsible for bootstrapping (initializing) your project after -it's been checked out. Here, you should install or clone any dependencies that -are required for a working build and development environment. - -By default, the script will verify that [xctool][] is installed, then initialize -and update submodules recursively. If any submodules contain `script/bootstrap`, -that will be run as well. - -To check that other tools are installed, you can set the `REQUIRED_TOOLS` -environment variable before running `script/bootstrap`, or edit it within the -script directly. Note that no installation is performed automatically, though -this can always be added within your specific project. - -### cibuild - -This script is responsible for building the project, as you would want it built -for continuous integration. This is preferable to putting the logic on the CI -server itself, since it ensures that any changes are versioned along with the -source. - -By default, the script will run [`bootstrap`](#bootstrap), look for any Xcode -workspace or project in the working directory, then build all targets/schemes -(as found by `xcodebuild -list`) using [xctool][]. - -You can also specify the schemes to build by passing them into the script: - -```sh -script/cibuild ReactiveCocoa-Mac ReactiveCocoa-iOS -``` - -As with the `bootstrap` script, there are several environment variables that can -be used to customize behavior. They can be set on the command line before -invoking the script, or the defaults changed within the script directly. - -## Getting Started - -To add the scripts to your project, read the contents of this repository into -a `script` folder: - -``` -$ git remote add objc-build-scripts https://github.com/jspahrsummers/objc-build-scripts.git -$ git fetch objc-build-scripts -$ git read-tree --prefix=script/ -u objc-build-scripts/master -``` - -Then commit the changes, to incorporate the scripts into your own repository's -history. You can also freely tweak the scripts for your specific project's -needs. - -To merge in upstream changes later: - -``` -$ git fetch -p objc-build-scripts -$ git merge --ff --squash -Xsubtree=script objc-build-scripts/master -``` - -[xctool]: https://github.com/facebook/xctool diff --git a/script/bootstrap b/script/bootstrap index 31deaf40f..aca53e814 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -1,6 +1,6 @@ #!/bin/bash -export SCRIPT_DIR=$(dirname "$0") +export SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) ## ## Configuration Variables @@ -9,8 +9,9 @@ export SCRIPT_DIR=$(dirname "$0") config () { # A whitespace-separated list of executables that must be present and locatable. - : ${REQUIRED_TOOLS="xctool cmake"} - + # These will each be installed through Homebrew if not found. + : ${REQUIRED_TOOLS="cmake libssh2 libtool autoconf automake pkg-config"} + export REQUIRED_TOOLS } @@ -22,51 +23,63 @@ main () { config - if [ -n "$REQUIRED_TOOLS" ] + local submodules=$(git submodule status) + local result=$? + + if [ "$result" -ne "0" ] then - echo "*** Checking dependencies..." - check_deps + exit $result fi - local submodules=$(git submodule status 2>/dev/null) if [ -n "$submodules" ] then echo "*** Updating submodules..." update_submodules fi + + if [ -n "$REQUIRED_TOOLS" ] + then + echo "*** Checking dependencies..." + check_deps + fi } check_deps () { - for tool in $REQUIRED_TOOLS - do - which -s "$tool" - if [ "$?" -ne "0" ] - then - echo "*** Error: $tool not found. Please install it and bootstrap again." - exit 1 - fi - done + # Check if Homebrew is installed + which -s brew + local result=$? + + if [ "$result" -ne "0" ] + then + echo + echo "Homebrew is not installed (http://brew.sh). You will need to manually ensure the following tools are installed:" + echo " $REQUIRED_TOOLS" + echo + echo "Additionally, the following libssh2 files must be symlinked under /usr/local:" + echo " lib/libssh2.a include/libssh2.h include/libssh2_sftp.h include/libssh2_publickey.h" + exit $result + fi # Ensure that we have libgit2's dependencies installed. - installed=`brew list` - libs="libssh2 libtool autoconf automake" + installed=`brew list --formula` - for lib in $libs + for tool in $REQUIRED_TOOLS do # Skip packages that are already installed. - echo "$installed" | grep -q "$lib" && code=$? || code=$? + echo "$installed" | grep -q "$tool" && code=$? || code=$? if [ "$code" -eq "0" ] then + echo "*** $tool is available" continue elif [ "$code" -ne "1" ] then exit $code fi - echo "*** Installing $lib with Homebrew..." - brew install "$lib" + echo "*** Installing $tool with Homebrew..." + brew install "$tool" done brew_prefix=`brew --prefix` @@ -107,7 +120,9 @@ bootstrap_submodule () update_submodules () { - git submodule sync --quiet && git submodule update --init && git submodule foreach --quiet bootstrap_submodule + git submodule sync --quiet && \ + git submodule update --init && \ + git submodule foreach --quiet bootstrap_submodule } export -f bootstrap_submodule diff --git a/script/cibuild b/script/cibuild index f73430352..0897947e5 100755 --- a/script/cibuild +++ b/script/cibuild @@ -1,143 +1,77 @@ -#!/bin/bash +#!/bin/bash -ex +# +# script/cibuild +# ObjectiveGit +# +# Executes the build and runs tests for Mac and iOS. Designed to be invoked by +# Travis as a matrix build so that the two platform builds can run in parallel. +# +# Dependent tools & scripts: +# - script/bootstrap +# - script/update_libssl_ios +# - [xcodebuild](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/xcodebuild.1.html) +# - xcpretty (gem) +# - xcpretty-travis-formatter (gem) +# +# Environment Variables: +# - SCHEME: specifies which Xcode scheme to build. Set to one of the following: +# - ObjectiveGit Mac +# - ObjectiveGit iOS +# - TRAVIS: indicates when the build is being run by travis, used to invoke +# the xcpretty-travis-formatter gem for output. + +if [ -z "$SCHEME" ]; then + echo "The SCHEME environment variable is empty. Please set this to one of:" + echo "- ObjectiveGit Mac" + echo "- ObjectiveGit iOS" + exit 1 +fi -export SCRIPT_DIR=$(dirname "$0") -. "$SCRIPT_DIR/xcode_functions.sh" ## ## Configuration Variables ## -SCHEMES="$@" +set -o pipefail +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +XCWORKSPACE="ObjectiveGitFramework.xcworkspace" +XCODE_OPTIONS=$(RUN_CLANG_STATIC_ANALYZER=NO ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO) -config () -{ - # The workspace to build. - # - # If not set and no workspace is found, the -workspace flag will not be passed - # to `xctool`. - # - # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will - # take precedence. - : ${XCWORKSPACE=$(find_pattern "*.xcworkspace")} - - # The project to build. - # - # If not set and no project is found, the -project flag will not be passed - # to `xctool`. - # - # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will - # take precedence. - : ${XCODEPROJ=$(find_pattern "*.xcodeproj")} - - # A bootstrap script to run before building. - # - # If this file does not exist, it is not considered an error. - : ${BOOTSTRAP="$SCRIPT_DIR/bootstrap"} - - # Extra options to pass to xctool. - : ${XCTOOL_OPTIONS="RUN_CLANG_STATIC_ANALYZER=NO"} - - # A whitespace-separated list of default schemes to build. - # - # Individual names can be quoted to avoid word splitting. - : ${SCHEMES:=ObjectiveGit ObjectiveGit-iOS} - - export XCWORKSPACE - export XCODEPROJ - export BOOTSTRAP - export XCTOOL_OPTIONS - export SCHEMES -} +if [ -n "$TRAVIS" ]; then + # Use a special formatter when running on TravisCI + XCPRETTY_FORMAT_OPTIONS="-f `xcpretty-travis-formatter`" +else + XCPRETTY_FORMAT_OPTIONS="--color" +fi ## ## Build Process ## -main () -{ - config - - if [ -f "$BOOTSTRAP" ] - then - echo "*** Bootstrapping..." - "$BOOTSTRAP" || exit $? - fi - - echo "*** The following schemes will be built:" - echo "$SCHEMES" | xargs -n 1 echo " " - echo - - echo "$SCHEMES" | xargs -n 1 | ( - local status=0 - - while read scheme - do - build_scheme "$scheme" || status=1 - done - - exit $status - ) -} - -find_pattern () -{ - ls -d $1 2>/dev/null | head -n 1 -} - -run_xctool () -{ - if [ -n "$XCWORKSPACE" ] - then - xctool -workspace "$XCWORKSPACE" $XCTOOL_OPTIONS "$@" 2>&1 - elif [ -n "$XCODEPROJ" ] - then - xctool -project "$XCODEPROJ" $XCTOOL_OPTIONS "$@" 2>&1 - else - echo "*** No workspace or project file found." - exit 1 - fi -} - -parse_build () -{ - awk -f "$SCRIPT_DIR/xctool.awk" 2>&1 >/dev/null -} - -build_scheme () -{ - local scheme=$1 - - echo "*** Cleaning $scheme..." - run_xctool -scheme "$scheme" clean >/dev/null || exit $? - - echo "*** Building and testing $scheme..." - echo - - local sdkflag= - local action=test - - # Determine whether we can run unit tests for this target. - run_xctool -scheme "$scheme" run-tests | parse_build - - local awkstatus=$? - - if [ "$awkstatus" -ne "0" ] - then - # Unit tests aren't supported. - action=build - fi - - if [ "$awkstatus" -eq "1" ] - then - # Build for iOS. - sdkflag="-sdk iphonesimulator" - fi - - run_xctool $sdkflag -scheme "$scheme" $action -} - -export -f build_scheme -export -f run_xctool -export -f parse_build - -main +echo "*** Bootstrapping..." +"$SCRIPT_DIR/bootstrap" + +if [ "$SCHEME" == "ObjectiveGit Mac" ]; then + echo "*** Building and testing $SCHEME..." + echo + + xcodebuild -workspace "$XCWORKSPACE" \ + -scheme "$SCHEME" \ + ${XCODE_OPTIONS[*]} \ + build test \ + 2>&1 #| xcpretty $XCPRETTY_FORMAT_OPTIONS +elif [ "$SCHEME" == "ObjectiveGit iOS" ]; then + echo "*** Prebuilding OpenSSL" + "$SCRIPT_DIR/update_libssl_ios" + + echo "*** Building and testing $SCHEME..." + echo + + xcodebuild -workspace "$XCWORKSPACE" \ + -scheme "$SCHEME" \ + -destination "platform=iOS Simulator,name=iPhone 11" \ + -sdk iphonesimulator \ + ${XCODE_OPTIONS[*]} \ + build test \ + 2>&1 #| xcpretty $XCPRETTY_FORMAT_OPTIONS +fi diff --git a/script/clean_externals b/script/clean_externals new file mode 100755 index 000000000..a25b6c530 --- /dev/null +++ b/script/clean_externals @@ -0,0 +1,22 @@ +#!/bin/bash -ex + +# +# clean_externals +# ObjectiveGit +# +# Removes the outputs from the various static library targets. +# Necessary when switching platforms/architectures as Xcode does not clean +# these for you. +# + +# A list of external static libraries included in the SwiftGit2 framework +libraries=( + External/libgit2.a + External/libgit2-ios/libgit2-ios.a + External/libssh2-ios/lib/libssh2-ios.a + External/ios-openssl/lib/libssl.a + External/ios-openssl/lib/libcrypto.a + External/ios-openssl/include +) + +rm -vrf "${libraries[@]}" diff --git a/script/ios_build_functions.sh b/script/ios_build_functions.sh index 36f5b14c7..515ca81b1 100755 --- a/script/ios_build_functions.sh +++ b/script/ios_build_functions.sh @@ -1,6 +1,6 @@ #!/bin/bash -SCRIPT_DIR=$(dirname "$0") +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) source "${SCRIPT_DIR}/xcode_functions.sh" function setup_build_environment () @@ -9,7 +9,7 @@ function setup_build_environment () # e.g. via brew. Xcode's Run Script phase doesn't seem to honor # ~/.MacOSX/environment.plist PATH="/usr/local/bin:/opt/boxen/homebrew/bin:$PATH" - + pushd "$SCRIPT_DIR/.." > /dev/null ROOT_PATH="$PWD" popd > /dev/null @@ -22,7 +22,7 @@ function setup_build_environment () MACOSX_DEPLOYMENT_TARGET="" XCODE_MAJOR_VERSION=$(xcode_major_version) - + CAN_BUILD_64BIT="0" # If IPHONEOS_DEPLOYMENT_TARGET has not been specified @@ -32,7 +32,7 @@ function setup_build_environment () then IPHONEOS_DEPLOYMENT_TARGET="6.0" fi - + # Determine if we can be building 64-bit binaries if [ "${XCODE_MAJOR_VERSION}" -ge "5" ] && [ $(echo ${IPHONEOS_DEPLOYMENT_TARGET} '>=' 6.0 | bc -l) == "1" ] then @@ -46,12 +46,19 @@ function setup_build_environment () # builds to be first ARCHS="x86_64 ${ARCHS} arm64" fi + + # Setup a shared area for our build artifacts + INSTALL_PATH="${ROOT_PATH}/External/build" + mkdir -p "${INSTALL_PATH}" + mkdir -p "${INSTALL_PATH}/log" + mkdir -p "${INSTALL_PATH}/include" + mkdir -p "${INSTALL_PATH}/lib/pkgconfig" } function build_all_archs () { setup_build_environment - + local setup=$1 local build_arch=$2 local finish_build=$3 @@ -81,10 +88,17 @@ function build_all_archs () SDKNAME="${PLATFORM}${SDKVERSION}" SDKROOT="$(ios_sdk_path ${SDKNAME})" - + + LOG="${INSTALL_PATH}/log/${LIBRARY_NAME}-${ARCH}.log" + [ -f "${LOG}" ] && rm "${LOG}" + echo "Building ${LIBRARY_NAME} for ${SDKNAME} ${ARCH}" + echo "Build log can be found in ${LOG}" echo "Please stand by..." + ARCH_INSTALL_PATH="${INSTALL_PATH}/${SDKNAME}-${ARCH}.sdk" + mkdir -p "${ARCH_INSTALL_PATH}" + # run the per arch build command eval $build_arch done diff --git a/script/pkg-config-static b/script/pkg-config-static new file mode 100755 index 000000000..64c6ea205 --- /dev/null +++ b/script/pkg-config-static @@ -0,0 +1,2 @@ +#!/bin/sh +pkg-config --static "$@" diff --git a/script/repackage-dylibs.rb b/script/repackage-dylibs.rb new file mode 100755 index 000000000..3af9ef430 --- /dev/null +++ b/script/repackage-dylibs.rb @@ -0,0 +1,124 @@ +#!/usr/bin/ruby + +# This script looks up an executable's list of shared libraries, copies +# non-standard ones (ie. anything not under /usr or /System/) into the target's +# bundle and updates the executable install_name to point to the "packaged" +# version. + +# Usage: +# Add the script as a Run Script build phase in the target using Xcode. + +# FIXMEs: +# - only handles dylibs +# - only tested against a framework target +# - doesn't care about codesigning + + +require 'fileutils' +require 'ostruct' + +def err(msg) + puts "error: " + msg + exit 1 +end + +def warn(msg) + puts "warning: " + msg +end + +def note(msg) + puts "note: " + msg +end + +envvars = %w( + TARGET_BUILD_DIR + EXECUTABLE_PATH + FRAMEWORKS_FOLDER_PATH +) + +envvars.each do |var| + raise "Must be run in an Xcode Run Phase" unless ENV[var] + Kernel.const_set var, ENV[var] +end + +TARGET_EXECUTABLE_PATH = File.join(TARGET_BUILD_DIR, EXECUTABLE_PATH) +TARGET_FRAMEWORKS_PATH = File.join(TARGET_BUILD_DIR, FRAMEWORKS_FOLDER_PATH) + +def extract_link_dependencies(executable) + deps = `otool -L "#{executable}"` + + lines = deps.split("\n").map(&:strip) + lines.shift + # lines.shift + lines.map do |dep| + path, compat, current = /^(.*) \(compatibility version (.*), current version (.*)\)$/.match(dep)[1..3] + err "Failed to parse #{dep}" if path.nil? + + dep = OpenStruct.new + dep.is_self = (File.basename(path) == File.basename(executable)) + dep.executable = executable + dep.install_name = path + dep.current_version = current + dep.compat_version = compat + dep.type = File.extname(path) + dep.name = File.basename(path) + dep.is_packaged = !!(dep.install_name =~ /^@rpath/) + dep.path = if dep.install_name =~ /^@rpath/ + File.join(TARGET_FRAMEWORKS_PATH, dep.name) + else + dep.install_name + end + + dep + end +end + +def repackage_dependency(dep) + return if dep.is_self or dep.is_packaged or dep.path =~ /^(\/usr\/lib|\/System\/Library)/ + + note "Packaging #{dep.name}…" + + FileUtils.mkdir(TARGET_FRAMEWORKS_PATH) unless Dir.exist?(TARGET_FRAMEWORKS_PATH) + packaged_path = File.join(TARGET_FRAMEWORKS_PATH, dep.name) + + case dep.type + when ".dylib" + if File.exist? packaged_path + warn "#{dep.path} already in Frameworks directory, removing" + FileUtils.rm packaged_path + end + + note "Copying #{dep[:path]} to TARGET_FRAMEWORKS_PATH" + FileUtils.cp dep[:path], TARGET_FRAMEWORKS_PATH + FileUtils.chmod "u=rw", packaged_path + + out = `install_name_tool -change "#{dep.path}" "@rpath/#{dep.name}" "#{dep.executable}"` + if $? != 0 + err "install_name_tool failed with error #{$?}:\n#{out}" + end + + dep.path = packaged_path + dep.install_name = "@rpath/#{dep.name}" + dep.is_packaged = true + else + warn "Unhandled type #{dep.type} for #{dep.path}, ignoring" + end +end + +def fix_install_id(dep) + note "Fixing #{dep.name} install_name id…" + out = `install_name_tool -id "@rpath/#{dep.name}" "#{dep.executable}"` + if $? != 0 + err "install_name_tool failed with error #{$?}:\n#{out}" + end +end + +deps = extract_link_dependencies(TARGET_EXECUTABLE_PATH) +while (dep = deps.shift) do + repackage_dependency dep + fix_install_id dep if dep.is_self and dep.executable != TARGET_EXECUTABLE_PATH + deps += extract_link_dependencies(dep[:path]) if dep.is_packaged and not dep.is_self +end + +note "Packaging done" +exit 0 diff --git a/script/schemes.awk b/script/schemes.awk deleted file mode 100644 index d101b4fd3..000000000 --- a/script/schemes.awk +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN { - FS = "\n"; -} - -/Targets:/ { - while (getline && $0 != "") { - if ($0 ~ /Test/) continue; - - sub(/^ +/, ""); - print "'" $0 "'"; - } -} diff --git a/script/targets.awk b/script/targets.awk deleted file mode 100644 index 117660dcd..000000000 --- a/script/targets.awk +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN { - FS = "\n"; -} - -/Targets:/ { - while (getline && $0 != "") { - if ($0 ~ /Tests/) continue; - - sub(/^ +/, ""); - print "'" $0 "'"; - } -} diff --git a/script/update_libgit2 b/script/update_libgit2 index 1b8742357..2c89cec38 100755 --- a/script/update_libgit2 +++ b/script/update_libgit2 @@ -7,7 +7,7 @@ set -e # ~/.MacOSX/environment.plist PATH="/usr/local/bin:$PATH" -if [ "External/libgit2.a" -nt "External/libgit2" ] +if [ "External/libgit2-mac.a" -nt "External/libgit2" ] then echo "No update needed." exit 0 @@ -22,13 +22,20 @@ fi mkdir build cd build -cmake -DBUILD_SHARED_LIBS:BOOL=OFF -DBUILD_CLAR:BOOL=OFF -DTHREADSAFE:BOOL=ON .. +# OpenSSL is keg-only, so add its pkgconfig location manually +export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig + +cmake -DBUILD_SHARED_LIBS:BOOL=OFF \ + -DLIBSSH2_INCLUDE_DIRS:PATH=/usr/local/include/ \ + -DBUILD_CLAR:BOOL=OFF \ + -DTHREADSAFE:BOOL=ON \ + .. cmake --build . product="libgit2.a" -install_path="../../${product}" -if [ "${product}" -nt "${install_path}" ]; then - cp -v "${product}" "${install_path}" +install_path="../../" +if [ "libgit2.a" -nt "${install_path}/libgit2-mac.a" ]; then + cp -v "libgit2.a" "${install_path}/libgit2-mac.a" fi echo "libgit2 has been updated." diff --git a/script/update_libgit2_ios b/script/update_libgit2_ios index 2721a0b17..2586f128b 100755 --- a/script/update_libgit2_ios +++ b/script/update_libgit2_ios @@ -3,76 +3,68 @@ set -e # source the common build functions -SCRIPT_DIR=$(dirname "$0") +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) source "${SCRIPT_DIR}/ios_build_functions.sh" function setup () { - if [ "${ROOT_PATH}/External/libgit2-ios.a" -nt "${ROOT_PATH}/External/libgit2" ] + if [ "${INSTALL_PATH}/lib/libgit2.a" -nt "${ROOT_PATH}/External/libgit2" ] then echo "No update needed." exit 0 fi LIBRARY_NAME="libgit2" - LIB_PATH="${ROOT_PATH}/External/libgit2-ios" - rm -rf "${LIB_PATH}" - - pushd "${ROOT_PATH}/External/libgit2" > /dev/null } function build_libgit2 () { - rm -rf "build" - mkdir "build" - pushd "build" > /dev/null + # Force CMake to rebuild + rm -rf "${ARCH_INSTALL_PATH}/libgit2" - # LOL Cmake - if [ "${ARCH}" != "i386" ] && [ "${ARCH}" != "x86_64" ] - then - SYS_ROOT="-DCMAKE_OSX_SYSROOT=${SDKROOT}" - fi + mkdir -p "${ARCH_INSTALL_PATH}/libgit2" + pushd "${ARCH_INSTALL_PATH}/libgit2" > /dev/null - # install the each built arch somewhere sane - INSTALL_PREFIX="${LIB_PATH}/${SDKNAME}-${ARCH}.sdk" + # LOL Cmake + SYS_ROOT="-DCMAKE_OSX_SYSROOT=${SDKROOT}" - mkdir -p "${INSTALL_PREFIX}" + # We only want to discover our static library + export PKG_CONFIG="${SCRIPT_DIR}"/pkg-config-static - LOG="${INSTALL_PREFIX}/build-libgit2.log" - echo "$LOG" + # Limit pkg-config to what we're currently expecting it to use + # PKG_CONFIG_PATH="${INSTALL_PATH}/lib/pkgconfig/" + # LDFLAGS="-L $(xcrun --sdk iphoneos --show-sdk-path)/usr/lib" + # export PKG_CONFIG_PATH + # export LDFLAGS - cmake -DCMAKE_C_COMPILER=clang \ + cmake \ -DCMAKE_C_COMPILER_WORKS:BOOL=ON \ -DBUILD_SHARED_LIBS:BOOL=OFF \ - -DOPENSSL_INCLUDE_DIR:PATH=../../External/ios-openssl/include/ \ - -DCMAKE_LIBRARY_PATH:PATH=../../External/libssh2-ios/lib/ \ - -DCMAKE_INCLUDE_PATH:PATH=../../External/libssh2-ios/include/libssh2/ \ - -DOPENSSL_SSL_LIBRARY:FILEPATH=../../External/ios-openssl/lib/libssl.a \ - -DCMAKE_LIBRARY_PATH:PATH="${SDKROOT}/usr/lib/" \ - -DOPENSSL_CRYPTO_LIBRARY:FILEPATH=../../External/ios-openssl/lib/libcrypto.a \ - -DCMAKE_INSTALL_PREFIX:PATH="${INSTALL_PREFIX}/" \ + -DCMAKE_PREFIX_PATH:PATH="${INSTALL_PATH}/" \ + -DPKG_CONFIG_USE_CMAKE_PREFIX_PATH:BOOL=ON \ + -DCMAKE_INSTALL_PREFIX:PATH="${ARCH_INSTALL_PATH}/" \ -DBUILD_CLAR:BOOL=OFF \ -DTHREADSAFE:BOOL=ON \ + -DCURL:BOOL=OFF \ + -DCMAKE_C_FLAGS:STRING="-fembed-bitcode" \ "${SYS_ROOT}" \ -DCMAKE_OSX_ARCHITECTURES:STRING="${ARCH}" \ - .. >> "${LOG}" 2>&1 + "${ROOT_PATH}/External/libgit2" >> "${LOG}" 2>&1 cmake --build . --target install >> "${LOG}" 2>&1 + popd > /dev/null # push the built library into the list - BUILT_LIB_PATHS+=("${INSTALL_PREFIX}/lib/libgit2.a") - popd > /dev/null + BUILT_LIB_PATHS+=("${ARCH_INSTALL_PATH}/lib/libgit2.a") } function fat_binary () { echo "Building fat binary..." - lipo -create "${BUILT_LIB_PATHS[@]}" -output "${ROOT_PATH}/External/libgit2-ios.a" + lipo -create "${BUILT_LIB_PATHS[@]}" -output "${INSTALL_PATH}/lib/libgit2.a" echo "Building done." - - popd > /dev/null } build_all_archs setup build_libgit2 fat_binary diff --git a/script/update_libssh2_ios b/script/update_libssh2_ios index 18d8a9fc2..b015047c5 100755 --- a/script/update_libssh2_ios +++ b/script/update_libssh2_ios @@ -3,12 +3,12 @@ set -e # source the common build functions -SCRIPT_DIR=$(dirname "$0") +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) source "${SCRIPT_DIR}/ios_build_functions.sh" function setup () { - if [ -f "${ROOT_PATH}/External/libssh2-ios/lib/libssh2-ios.a" ] + if [ -f "${INSTALL_PATH}/lib/libssh2.a" ] then echo "No update needed." exit 0 @@ -16,38 +16,36 @@ function setup () LIBRARY_NAME="libssh2" } -function build_ssh2 () +function build_ssh2 () { - mkdir -p "${ROOT_PATH}/External/libssh2-ios/lib" "${ROOT_PATH}/External/libssh2-ios/lib" "${ROOT_PATH}/External/libssh2-ios/src" + cp -R "${ROOT_PATH}/External/libssh2" "${ARCH_INSTALL_PATH}/libssh2" + pushd "${ARCH_INSTALL_PATH}/libssh2" > /dev/null - rm -rf "${ROOT_PATH}/External/libssh2-ios/src/libssh2" - cp -R "${ROOT_PATH}/External/libssh2" "${ROOT_PATH}/External/libssh2-ios/src/" - pushd "${ROOT_PATH}/External/libssh2-ios/src/libssh2" > /dev/null - - export CFLAGS="-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -miphoneos-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" - export CPPFLAGS="-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -miphoneos-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" - - mkdir -p "${ROOT_PATH}/External/libssh2-ios/bin/${SDKNAME}-${ARCH}.sdk" - LOG="${ROOT_PATH}/External/libssh2-ios/bin/${SDKNAME}-${ARCH}.sdk/build-libssh2.log" - - echo "${LOG}" + export CFLAGS="-arch ${ARCH} -fembed-bitcode -pipe -no-cpp-precomp -isysroot ${SDKROOT} -miphoneos-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" + export CPPFLAGS="-arch ${ARCH} -fembed-bitcode -pipe -no-cpp-precomp -isysroot ${SDKROOT} -miphoneos-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ./buildconf >> "${LOG}" 2>&1 - ./configure --host=${HOST} --prefix="${ROOT_PATH}/External/libssh2-ios/bin/${SDKNAME}-${ARCH}.sdk" --with-openssl --with-libssl-prefix="${ROOT_PATH}/External/ios-openssl" --disable-shared --enable-static >> "${LOG}" 2>&1 + ./configure --host=${HOST} --prefix="${ARCH_INSTALL_PATH}" --with-openssl --with-libssl-prefix="${INSTALL_PATH}" --disable-shared --enable-static >> "${LOG}" 2>&1 make >> "${LOG}" 2>&1 make install >> "${LOG}" 2>&1 popd > /dev/null - BUILT_LIBS+=("${ROOT_PATH}/External/libssh2-ios/bin/${SDKNAME}-${ARCH}.sdk/lib/libssh2.a") + rm -rf "${ARCH_INSTALL_PATH}/libssh2" + + BUILT_LIBS+=("${ARCH_INSTALL_PATH}/lib/libssh2.a") } function fat_binary () { + echo "Copying headers & pkg-config files" + + cp -r "${ARCH_INSTALL_PATH}"/include/libssh*.h "${INSTALL_PATH}/include/" + cp "${ARCH_INSTALL_PATH}/lib/pkgconfig/libssh2.pc" "${INSTALL_PATH}/lib/pkgconfig/" + perl -i -pe "s|^(prefix=${INSTALL_PATH}).*$|\$1|g" "${INSTALL_PATH}/lib/pkgconfig/libssh2.pc" >> "${LOG}" 2>&1 + echo "Building fat binary..." - lipo -create "${BUILT_LIBS[@]}" -output "${ROOT_PATH}/External/libssh2-ios/lib/libssh2-ios.a" - mkdir -p "${ROOT_PATH}/External/libssh2-ios/include/libssh2" - cp -R "${ROOT_PATH}/External/libssh2-ios/bin/iphonesimulator${SDKVERSION}-i386.sdk/include/libssh2.h" "${ROOT_PATH}/External/libssh2-ios/include/libssh2/" + lipo -create "${BUILT_LIBS[@]}" -output "${INSTALL_PATH}/lib/libssh2.a" echo "Building done." } diff --git a/script/update_libssl_ios b/script/update_libssl_ios index cf8eaff36..2b4593683 100755 --- a/script/update_libssl_ios +++ b/script/update_libssl_ios @@ -8,30 +8,21 @@ source "${SCRIPT_DIR}/ios_build_functions.sh" function setup () { - if [ -f "${ROOT_PATH}/External/ios-openssl/lib/libssl.a" ] && [ -f "${ROOT_PATH}/External/ios-openssl/lib/libcrypto.a" ] && [ -d "${ROOT_PATH}/External/ios-openssl/include" ] + if [ -f "${INSTALL_PATH}/lib/libssl.a" -a \ + -f "${INSTALL_PATH}/lib/libcrypto.a" -a \ + -d "${INSTALL_PATH}/include/openssl" ] then echo "No update needed." exit 0 fi LIBRARY_NAME="OpenSSL" - - rm -rf "${ROOT_PATH}/External/ios-openssl/include" "External/ios-openssl/lib" } -function cleanup () +function build_ssl () { - rm -rf "/tmp/openssl" - rm -rf "/tmp/openssl-*.log" -} - -function build_ssl () -{ - rm -rf "/tmp/openssl" - cp -r "${ROOT_PATH}/External/openssl" "/tmp/" - pushd "/tmp/openssl" > /dev/null - - LOG="/tmp/openssl-${ARCH}.log" + cp -r "${ROOT_PATH}/External/openssl" "${ARCH_INSTALL_PATH}/openssl" + pushd "${ARCH_INSTALL_PATH}/openssl" > /dev/null if [ "${ARCH}" == "arm64" ] || [ "${ARCH}" == "x86_64" ] then @@ -42,36 +33,36 @@ function build_ssl () CONFIG="no-gost no-asm" perl -i -pe 's|static volatile sig_atomic_t intr_signal|static volatile int intr_signal|' crypto/ui/ui_openssl.c fi - echo "$LOG" - ./Configure ${HOST} ${CONFIG} --openssldir="/tmp/openssl-${ARCH}" >> "${LOG}" 2>&1 - perl -i -pe "s|^CC= gcc|CC= ${CLANG} -miphoneos-version-min=${IPHONEOS_DEPLOYMENT_TARGET} -arch ${ARCH} |g" Makefile >> "${LOG}" 2>&1 + ./Configure ${HOST} ${CONFIG} --openssldir="${ARCH_INSTALL_PATH}" >> "${LOG}" 2>&1 + perl -i -pe "s|^CC= gcc|CC= ${CLANG} -miphoneos-version-min=${IPHONEOS_DEPLOYMENT_TARGET} -arch ${ARCH} -fembed-bitcode |g" Makefile >> "${LOG}" 2>&1 perl -i -pe "s|^CFLAG= (.*)|CFLAG= -isysroot ${SDKROOT} \$1|g" Makefile >> "${LOG}" 2>&1 make >> "${LOG}" 2>&1 - make install >> "${LOG}" 2>&1 + make install_sw >> "${LOG}" 2>&1 popd > /dev/null - rm -rf "/tmp/openssl" - BUILT_CRYPTO_PATHS+=("/tmp/openssl-${ARCH}/lib/libcrypto.a") - BUILT_SSL_PATHS+=("/tmp/openssl-${ARCH}/lib/libssl.a") + rm -rf "${ARCH_INSTALL_PATH}/openssl" + + BUILT_CRYPTO_PATHS+=("${ARCH_INSTALL_PATH}/lib/libcrypto.a") + BUILT_SSL_PATHS+=("${ARCH_INSTALL_PATH}/lib/libssl.a") } function fat_binary () { - echo "Building fat binary..." + echo "Copying headers & pkg-config files" + cp -r "${ARCH_INSTALL_PATH}/include/openssl" "${INSTALL_PATH}/include/" + for pkgfile in "${ARCH_INSTALL_PATH}"/lib/pkgconfig/*.pc; do + cp "${pkgfile}" "${INSTALL_PATH}/lib/pkgconfig/" + perl -i -pe "s|^(prefix=${INSTALL_PATH}).*$|\$1|g" "${INSTALL_PATH}/lib/pkgconfig/$(basename "${pkgfile}")" >> "${LOG}" 2>&1 + done - mkdir -p "${ROOT_PATH}/External/ios-openssl/include" - cp -r /tmp/openssl-i386/include/openssl "${ROOT_PATH}/External/ios-openssl/include/" - - mkdir -p "${ROOT_PATH}/External/ios-openssl/lib" + echo "Building fat binary..." - lipo -create "${BUILT_CRYPTO_PATHS[@]}" -output "${ROOT_PATH}/External/ios-openssl/lib/libcrypto.a" - lipo -create "${BUILT_SSL_PATHS[@]}" -output "${ROOT_PATH}/External/ios-openssl/lib/libssl.a" + lipo -create "${BUILT_CRYPTO_PATHS[@]}" -output "${INSTALL_PATH}/lib/libcrypto.a" + lipo -create "${BUILT_SSL_PATHS[@]}" -output "${INSTALL_PATH}/lib/libssl.a" echo "Building done." } -cleanup build_all_archs setup build_ssl fat_binary -cleanup diff --git a/script/xcode_functions.sh b/script/xcode_functions.sh index e87697c90..2dc7e3b2c 100755 --- a/script/xcode_functions.sh +++ b/script/xcode_functions.sh @@ -14,13 +14,19 @@ function xcode_major_version () xcode_version | awk -F '.' '{ print $1 }' } -# Returns the latest iOS SDK version available -# via xcodebuild +# Returns the latest iOS SDK version available via xcodebuild. function ios_sdk_version () { - # This relies on the fact that the latest iPhone SDK - # is the last thing listed before the Xcode version. - /usr/bin/xcodebuild -version -sdk 2> /dev/null | grep SDKVersion | tail -n 1 | awk '{ print $2 }' + # The grep command produces output like the following, singling out the + # SDKVersion of just the iPhone* SDKs: + # + # iPhoneOS9.0.sdk - iOS 9.0 (iphoneos9.0) + # SDKVersion: 9.0 + # -- + # iPhoneSimulator9.0.sdk - Simulator - iOS 9.0 (iphonesimulator9.0) + # SDKVersion: 9.0 + + /usr/bin/xcodebuild -version -sdk 2> /dev/null | grep -A 1 '^iPhone' | tail -n 1 | awk '{ print $2 }' } # Returns the path to the specified iOS SDK name diff --git a/script/xcodebuild.awk b/script/xcodebuild.awk deleted file mode 100644 index c746b09aa..000000000 --- a/script/xcodebuild.awk +++ /dev/null @@ -1,35 +0,0 @@ -# Exit statuses: -# -# 0 - No errors found. -# 1 - Build or test failure. Errors will be logged automatically. -# 2 - Untestable target. Retry with the "build" action. - -BEGIN { - status = 0; -} - -{ - print; - fflush(stdout); -} - -/is not valid for Testing/ { - exit 2; -} - -/[0-9]+: (error|warning):/ { - errors = errors $0 "\n"; -} - -/(TEST|BUILD) FAILED/ { - status = 1; -} - -END { - if (length(errors) > 0) { - print "\n*** All errors:\n" errors; - } - - fflush(stdout); - exit status; -} diff --git a/script/xctool.awk b/script/xctool.awk deleted file mode 100644 index f6132589d..000000000 --- a/script/xctool.awk +++ /dev/null @@ -1,25 +0,0 @@ -# Exit statuses: -# -# 0 - No errors found. -# 1 - Wrong SDK. Retry with SDK `iphonesimulator`. -# 2 - Missing target. - -BEGIN { - status = 0; -} - -{ - print; -} - -/Testing with the '(.+)' SDK is not yet supported/ { - status = 1; -} - -/does not contain a target named/ { - status = 2; -} - -END { - exit status; -}