diff --git a/Classes/GTRepository.h b/Classes/GTRepository.h index ed3d7b339..c38d678de 100644 --- a/Classes/GTRepository.h +++ b/Classes/GTRepository.h @@ -62,6 +62,15 @@ typedef enum { GTRepositoryResetTypeHard = GIT_RESET_HARD } GTRepositoryResetType; +// 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; + typedef void (^GTRepositoryStatusBlock)(NSURL *fileURL, GTRepositoryFileStatus status, BOOL *stop); @interface GTRepository : NSObject @@ -202,6 +211,31 @@ typedef void (^GTRepositoryStatusBlock)(NSURL *fileURL, GTRepositoryFileStatus s // Returns the signature. - (GTSignature *)userSignatureForNow; +// Stash the repository's changes. +// +// message - Optional message to be attributed to the item in the stash. +// stashFlag - The flags of stash to be used. +// error - If not NULL, it will be set on return to any error that occurred +// +// Returns commit of the stashed changes if successful, nil otherwise +- (GTCommit *)stashChangesWithMessage:(NSString *)message flags:(GTRepositoryStashFlag)flags error:(NSError **)error; + +// Enumerate over all the stashes in the repository +// +// block - A block to execute for each stash found, giving the stash's index and +// message along with its OID. Setting `stop` to YES will cause enumeration +// to stop after the block returns. +// +- (void)enumerateStashesUsingBlock:(void (^)(size_t 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 +// error - If not NULL, set to any error that occurs +// +// Returns YES if the stash was successfully dropped, NO otherwise +- (BOOL)dropStashAtIndex:(size_t)index error:(NSError **)error; + // Reloads all cached information about the receiver's submodules. // // Existing GTSubmodule objects from this repository will be mutated as part of diff --git a/Classes/GTRepository.m b/Classes/GTRepository.m index 963e679e9..9238323e2 100644 --- a/Classes/GTRepository.m +++ b/Classes/GTRepository.m @@ -43,8 +43,10 @@ #import "NSError+Git.h" #import "NSString+Git.h" -// The type of block passed to -enumerateSubmodulesRecursively:usingBlock:. +// Blocks typedef for -enumerateSubmodulesRecursively:usingBlock: and +// -enumerateStashesWithBlock:flags:error:. typedef void (^GTRepositorySubmoduleEnumerationBlock)(GTSubmodule *submodule, BOOL *stop); +typedef void (^GTRepositoryStashEnumerationBlock)(size_t index, NSString *message, GTOID *oid, BOOL *stop); // Used as a payload for submodule enumeration. // @@ -614,4 +616,49 @@ - (GTSignature *)userSignatureForNow { return [[GTSignature alloc] initWithName:name email:email time:[NSDate date]]; } +#pragma mark Stash + +- (GTCommit*)stashChangesWithMessage:(NSString *)message flags:(GTRepositoryStashFlag)flags error:(NSError **)error +{ + git_oid oid; + git_signature *sign = (git_signature *)[self userSignatureForNow].git_signature; + + int gitError = git_stash_save(&oid, self.git_repository, sign, [message UTF8String], flags); + if (gitError != GIT_OK) { + if (error != NULL) *error = [NSError git_errorFor:gitError withAdditionalDescription:@"Failed to stash."]; + return nil; + } + + GTCommit* commit = (GTCommit*)[self lookupObjectByOid:&oid error:error]; + + return commit; +} + +static int stashEnumerationCallback(size_t index, const char* message, const git_oid *stash_id, void *payload) { + GTRepositoryStashEnumerationBlock block = (__bridge GTRepositoryStashEnumerationBlock)payload; + + NSString *messageString = @(message); + GTOID *stash_oid = [[GTOID alloc] initWithGitOid:stash_id]; + BOOL stop = NO; + + block(index, messageString, stash_oid, &stop); + + return stop; +} + +- (void)enumerateStashesUsingBlock:(GTRepositoryStashEnumerationBlock)block { + NSParameterAssert(block != nil); + + git_stash_foreach(self.git_repository, &stashEnumerationCallback, &block); +} + +- (BOOL)dropStashAtIndex:(size_t)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 withAdditionalDescription:@"Failed to drop stash."]; + return NO; + } + return YES; +} + @end diff --git a/ObjectiveGitTests/GTRepositorySpec.m b/ObjectiveGitTests/GTRepositorySpec.m index 9f52a3cec..b1146f8d1 100644 --- a/ObjectiveGitTests/GTRepositorySpec.m +++ b/ObjectiveGitTests/GTRepositorySpec.m @@ -51,4 +51,14 @@ }); }); +describe(@"-stashChangesWithMessage:flags:error:", ^{ + it(@"should fail if there's nothing to stash (with default options)", ^{ + NSError *error = nil; + GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error]; + expect(stash).to.beNil(); + expect(error).notTo.beNil(); + expect(error.code).to.equal(GIT_ENOTFOUND); + }); +}); + SpecEnd