diff --git a/ObjectiveGit/GTBranch.h b/ObjectiveGit/GTBranch.h index d53977643..3b3be239d 100644 --- a/ObjectiveGit/GTBranch.h +++ b/ObjectiveGit/GTBranch.h @@ -50,6 +50,7 @@ NS_ASSUME_NONNULL_BEGIN @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; @@ -59,18 +60,16 @@ NS_ASSUME_NONNULL_BEGIN /// Designated initializer. /// /// ref - The branch reference to wrap. Must not be nil. -/// repo - The repository containing the branch. Must not be nil. /// /// Returns the initialized receiver. -- (instancetype _Nullable)initWithReference:(GTReference *)ref repository:(GTRepository *)repo NS_DESIGNATED_INITIALIZER; +- (instancetype _Nullable)initWithReference:(GTReference *)ref NS_DESIGNATED_INITIALIZER; /// Convenience class initializer. /// /// ref - The branch reference to wrap. Must not be nil. -/// repo - The repository containing the branch. Must not be nil. /// /// Returns an initialized instance. -+ (instancetype _Nullable)branchWithReference:(GTReference *)ref repository:(GTRepository *)repo; ++ (instancetype _Nullable)branchWithReference:(GTReference *)ref; /// Get the target commit for this branch /// @@ -79,6 +78,9 @@ NS_ASSUME_NONNULL_BEGIN /// 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 diff --git a/ObjectiveGit/GTBranch.m b/ObjectiveGit/GTBranch.m index eb42f3356..8e9e3d362 100644 --- a/ObjectiveGit/GTBranch.m +++ b/ObjectiveGit/GTBranch.m @@ -32,6 +32,7 @@ #import "GTRemote.h" #import "GTRepository.h" #import "NSError+Git.h" +#import "NSData+Git.h" #import "git2/branch.h" #import "git2/errors.h" @@ -65,8 +66,8 @@ + (NSString *)remoteNamePrefix { return @"refs/remotes/"; } -+ (instancetype)branchWithReference:(GTReference *)ref repository:(GTRepository *)repo { - return [[self alloc] initWithReference:ref repository:repo]; ++ (instancetype)branchWithReference:(GTReference *)ref { + return [[self alloc] initWithReference:ref]; } - (instancetype)init { @@ -74,21 +75,23 @@ - (instancetype)init { return nil; } -- (instancetype)initWithReference:(GTReference *)ref repository:(GTRepository *)repo { +- (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 { @@ -112,17 +115,12 @@ - (GTOID *)OID { } - (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 *)targetCommitWithError:(NSError **)error { @@ -142,6 +140,10 @@ - (NSUInteger)numberOfCommitsWithError:(NSError **)error { return [enumerator countRemainingObjects:error]; } +- (GTRepository *)repository { + return self.reference.repository; +} + - (GTBranchType)branchType { if (self.reference.remote) { return GTBranchTypeRemote; @@ -150,6 +152,10 @@ - (GTBranchType)branchType { } } +- (BOOL)isHEAD { + return (git_branch_is_head(self.reference.git_reference) ? YES : NO); +} + - (NSArray *)uniqueCommitsRelativeToBranch:(GTBranch *)otherBranch error:(NSError **)error { GTEnumerator *enumerator = [self.repository enumeratorForUniqueCommitsFromOID:self.OID relativeToOID:otherBranch.OID error:error]; return [enumerator allObjectsWithError:error]; @@ -165,6 +171,19 @@ - (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; + } + + _reference = [[GTReference alloc] initWithGitReference:git_ref repository:self.repository]; + + return YES; +} + - (GTBranch *)trackingBranchWithError:(NSError **)error success:(BOOL *)success { if (self.branchType == GTBranchTypeRemote) { if (success != NULL) *success = YES; @@ -194,7 +213,7 @@ - (GTBranch *)trackingBranchWithError:(NSError **)error success:(BOOL *)success if (success != NULL) *success = YES; - return [[self class] branchWithReference:[[GTReference alloc] initWithGitReference:trackingRef repository:self.repository] repository:self.repository]; + return [[self class] branchWithReference:[[GTReference alloc] initWithGitReference:trackingRef repository:self.repository]]; } - (BOOL)updateTrackingBranch:(GTBranch *)trackingBranch error:(NSError **)error { @@ -216,7 +235,7 @@ - (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 { diff --git a/ObjectiveGit/GTCommit.h b/ObjectiveGit/GTCommit.h index af84a0e4f..c9a666b76 100644 --- a/ObjectiveGit/GTCommit.h +++ b/ObjectiveGit/GTCommit.h @@ -39,9 +39,11 @@ NS_ASSUME_NONNULL_BEGIN @interface GTCommit : GTObject {} +@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; diff --git a/ObjectiveGit/GTCommit.m b/ObjectiveGit/GTCommit.m index 5a6e458a6..495877a22 100644 --- a/ObjectiveGit/GTCommit.m +++ b/ObjectiveGit/GTCommit.m @@ -53,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; @@ -117,6 +121,19 @@ - (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]; diff --git a/ObjectiveGit/GTEnumerator.h b/ObjectiveGit/GTEnumerator.h index 2ab38b841..4d8b27201 100644 --- a/ObjectiveGit/GTEnumerator.h +++ b/ObjectiveGit/GTEnumerator.h @@ -64,6 +64,9 @@ NS_ASSUME_NONNULL_BEGIN - (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. @@ -89,6 +92,21 @@ NS_ASSUME_NONNULL_BEGIN /// 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 @@ -106,6 +124,22 @@ NS_ASSUME_NONNULL_BEGIN /// 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; diff --git a/ObjectiveGit/GTEnumerator.m b/ObjectiveGit/GTEnumerator.m index 4b45a59f8..6c76530c1 100644 --- a/ObjectiveGit/GTEnumerator.m +++ b/ObjectiveGit/GTEnumerator.m @@ -53,6 +53,10 @@ - (instancetype)init { return nil; } +- (git_revwalk *)git_revwalk { + return self.walk; +} + - (instancetype)initWithRepository:(GTRepository *)repo error:(NSError **)error { NSParameterAssert(repo != nil); @@ -107,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); @@ -127,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 { diff --git a/ObjectiveGit/GTReference.h b/ObjectiveGit/GTReference.h index cc36b8d70..f273ca00d 100644 --- a/ObjectiveGit/GTReference.h +++ b/ObjectiveGit/GTReference.h @@ -54,9 +54,18 @@ NS_ASSUME_NONNULL_BEGIN @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 *reflog; diff --git a/ObjectiveGit/GTReference.m b/ObjectiveGit/GTReference.m index 63c63836d..d34d946a2 100644 --- a/ObjectiveGit/GTReference.m +++ b/ObjectiveGit/GTReference.m @@ -105,6 +105,18 @@ - (instancetype)initWithGitReference:(git_reference *)ref repository:(GTReposito 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; diff --git a/ObjectiveGit/GTRepository+Merging.m b/ObjectiveGit/GTRepository+Merging.m index 958f53ec6..d67225bb5 100644 --- a/ObjectiveGit/GTRepository+Merging.m +++ b/ObjectiveGit/GTRepository+Merging.m @@ -158,7 +158,7 @@ - (BOOL)mergeBranchIntoCurrentBranch:(GTBranch *)branch withError:(NSError **)er NSArray *parents = @[ localCommit, remoteCommit ]; // FIXME: This is stepping on the local tree - GTCommit *mergeCommit = [self createCommitWithTree:newTree message:message parents:parents updatingReferenceNamed:localBranch.name error:error]; + GTCommit *mergeCommit = [self createCommitWithTree:newTree message:message parents:parents updatingReferenceNamed:localBranch.reference.name error:error]; if (!mergeCommit) { return NO; } diff --git a/ObjectiveGit/GTRepository.m b/ObjectiveGit/GTRepository.m index 5e4a70b9b..e0b0a8582 100644 --- a/ObjectiveGit/GTRepository.m +++ b/ObjectiveGit/GTRepository.m @@ -325,7 +325,7 @@ - (id)lookUpObjectByGitOid:(const git_oid *)oid objectType:(GTObjectType)type er return nil; } - return [GTObject objectWithObj:obj inRepository:self]; + return [GTObject objectWithObj:obj inRepository:self]; } - (id)lookUpObjectByGitOid:(const git_oid *)oid error:(NSError **)error { @@ -377,7 +377,7 @@ - (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 { @@ -395,22 +395,55 @@ - (GTReference *)headReferenceWithError:(NSError **)error { 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; + } + + 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 { @@ -424,7 +457,7 @@ - (NSArray *)branchesWithPrefix:(NSString *)prefix error:(NSError **)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]; @@ -561,7 +594,7 @@ - (GTBranch *)createBranchNamed:(NSString *)name fromOID:(GTOID *)targetOID mess 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 { @@ -572,7 +605,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 { diff --git a/ObjectiveGit/GTTag.h b/ObjectiveGit/GTTag.h index 2b72bb526..bd7318e64 100644 --- a/ObjectiveGit/GTTag.h +++ b/ObjectiveGit/GTTag.h @@ -59,6 +59,9 @@ NS_ASSUME_NONNULL_BEGIN /// Returns the found object or nil on error. - (id _Nullable)objectByPeelingTagError:(NSError **)error; +/// 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)); diff --git a/ObjectiveGit/GTTag.m b/ObjectiveGit/GTTag.m index aa3312454..3c8c617c8 100644 --- a/ObjectiveGit/GTTag.m +++ b/ObjectiveGit/GTTag.m @@ -84,4 +84,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/ObjectiveGitTests/GTBranchSpec.m b/ObjectiveGitTests/GTBranchSpec.m index 09650c5f5..f143a03e7 100644 --- a/ObjectiveGitTests/GTBranchSpec.m +++ b/ObjectiveGitTests/GTBranchSpec.m @@ -35,6 +35,16 @@ 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")); @@ -163,7 +173,7 @@ expect(otherRef).notTo(beNil()); expect(error).to(beNil()); - GTBranch *otherBranch = [GTBranch branchWithReference:otherRef repository:repository]; + GTBranch *otherBranch = [GTBranch branchWithReference:otherRef]; expect(otherBranch).notTo(beNil()); BOOL success = NO; @@ -179,7 +189,7 @@ expect(remoteRef).notTo(beNil()); expect(error).to(beNil()); - GTBranch *remoteBranch = [GTBranch branchWithReference:remoteRef repository:repository]; + GTBranch *remoteBranch = [GTBranch branchWithReference:remoteRef]; expect(remoteBranch).notTo(beNil()); BOOL success = NO; @@ -262,28 +272,40 @@ }); }); -// 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(@"-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]; diff --git a/ObjectiveGitTests/GTCommitSpec.m b/ObjectiveGitTests/GTCommitSpec.m index 31e80e998..af4e8fc5c 100644 --- a/ObjectiveGitTests/GTCommitSpec.m +++ b/ObjectiveGitTests/GTCommitSpec.m @@ -31,6 +31,7 @@ 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")); @@ -60,7 +61,14 @@ expect(commit).notTo(beNil()); expect(error).to(beNil()); - expect(@(commit.parents.count)).to(equal(@2)); + 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", ^{ diff --git a/ObjectiveGitTests/GTEnumeratorSpec.m b/ObjectiveGitTests/GTEnumeratorSpec.m index dda0d67b6..e9ec1649a 100644 --- a/ObjectiveGitTests/GTEnumeratorSpec.m +++ b/ObjectiveGitTests/GTEnumeratorSpec.m @@ -31,10 +31,12 @@ GTReference *HEADRef = [repo headReferenceWithError:NULL]; expect(HEADRef).notTo(beNil()); - [enumerator pushSHA:HEADRef.targetOID.SHA error:NULL]; + 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()); }); describe(@"with a rev list", ^{ diff --git a/ObjectiveGitTests/GTRepositorySpec.m b/ObjectiveGitTests/GTRepositorySpec.m index efe61d1bf..cef89f86e 100644 --- a/ObjectiveGitTests/GTRepositorySpec.m +++ b/ObjectiveGitTests/GTRepositorySpec.m @@ -292,7 +292,7 @@ GTBranch *currentBranch = [repository currentBranchWithError:&error]; expect(currentBranch).notTo(beNil()); expect(error).to(beNil()); - expect(currentBranch.name).to(equal(@"refs/heads/master")); + expect(currentBranch.name).to(equal(@"master")); }); }); @@ -332,7 +332,7 @@ 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")); }); }); diff --git a/ObjectiveGitTests/GTTagSpec.m b/ObjectiveGitTests/GTTagSpec.m index b6ab16506..93b26e2f6 100644 --- a/ObjectiveGitTests/GTTagSpec.m +++ b/ObjectiveGitTests/GTTagSpec.m @@ -39,6 +39,18 @@ 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]; }); diff --git a/ObjectiveGitTests/GTUtilityFunctions.m b/ObjectiveGitTests/GTUtilityFunctions.m index 95889e6de..f6fbe4e46 100644 --- a/ObjectiveGitTests/GTUtilityFunctions.m +++ b/ObjectiveGitTests/GTUtilityFunctions.m @@ -16,7 +16,7 @@ CreateCommitBlock createCommitInRepository = ^ GTCommit * (NSString *message, NSData *fileData, NSString *fileName, GTRepository *repo) { GTReference *head = [repo headReferenceWithError:NULL]; - GTBranch *branch = [GTBranch branchWithReference:head repository:repo]; + GTBranch *branch = [GTBranch branchWithReference:head]; GTCommit *headCommit = [branch targetCommitWithError:NULL]; GTTreeBuilder *treeBuilder = [[GTTreeBuilder alloc] initWithTree:headCommit.tree repository:repo error:nil]; @@ -40,11 +40,10 @@ #pragma mark - Branch BranchBlock localBranchWithName = ^ GTBranch * (NSString *branchName, GTRepository *repo) { - NSString *reference = [GTBranch.localNamePrefix stringByAppendingString:branchName]; - NSArray *branches = [repo branchesWithPrefix:reference error:NULL]; - expect(branches).notTo(beNil()); - expect(@(branches.count)).to(equal(@1)); - expect(((GTBranch *)branches[0]).shortName).to(equal(branchName)); + 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 branches[0]; + return branch; };