Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Stash support #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Sep 29, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Classes/GTRepository+Stashing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// 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
61 changes: 61 additions & 0 deletions Classes/GTRepository+Stashing.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// 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
2 changes: 0 additions & 2 deletions Classes/GTRepository.m
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@
NSString *const GTRepositoryCloneOptionsCheckout = @"GTRepositoryCloneOptionsCheckout";
NSString *const GTRepositoryCloneOptionsTransportFlags = @"GTRepositoryCloneOptionsTransportFlags";

// The type of block passed to -enumerateSubmodulesRecursively:usingBlock:.
typedef void (^GTRepositorySubmoduleEnumerationBlock)(GTSubmodule *submodule, BOOL *stop);

typedef void (^GTRepositoryTagEnumerationBlock)(GTTag *tag, BOOL *stop);

// Used as a payload for submodule enumeration.
Expand Down
1 change: 1 addition & 0 deletions Classes/ObjectiveGit.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "git2.h"

#import <ObjectiveGit/GTRepository.h>
#import <ObjectiveGit/GTRepository+Stashing.h>
#import <ObjectiveGit/GTEnumerator.h>
#import <ObjectiveGit/GTCommit.h>
#import <ObjectiveGit/GTSignature.h>
Expand Down
16 changes: 16 additions & 0 deletions ObjectiveGitFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@
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 */; };
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 */; };
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 */; };
Expand Down Expand Up @@ -482,6 +487,9 @@
BDFAF9C7131C1868000508BC /* GTIndexEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTIndexEntry.h; sourceTree = "<group>"; };
BDFAF9C8131C1868000508BC /* GTIndexEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GTIndexEntry.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
D00F6815175D373C004DB9D6 /* GTReferenceSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTReferenceSpec.m; sourceTree = "<group>"; };
D015F7C817F695E800AD5E1F /* GTRepository+Stashing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Stashing.h"; sourceTree = "<group>"; };
D015F7C917F695E800AD5E1F /* GTRepository+Stashing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Stashing.m"; sourceTree = "<group>"; };
D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRepositoryStashingSpec.m; sourceTree = "<group>"; };
D03B7C401756AB370034A610 /* GTSubmoduleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTSubmoduleSpec.m; sourceTree = "<group>"; };
D040AF6F177B9779001AD9EB /* GTOIDSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTOIDSpec.m; sourceTree = "<group>"; };
D040AF77177B9A9E001AD9EB /* GTSignatureSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTSignatureSpec.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -682,6 +690,7 @@
D040AF6F177B9779001AD9EB /* GTOIDSpec.m */,
D040AF77177B9A9E001AD9EB /* GTSignatureSpec.m */,
D0F4E28917C7F24200BBDE30 /* NSErrorGitSpec.m */,
D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */,
);
path = ObjectiveGitTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -730,6 +739,8 @@
BDE4C062130EFE2C00851650 /* GTRepository.h */,
4DE864341794A37E00371A65 /* GTRepository+Private.h */,
BDE4C063130EFE2C00851650 /* GTRepository.m */,
D015F7C817F695E800AD5E1F /* GTRepository+Stashing.h */,
D015F7C917F695E800AD5E1F /* GTRepository+Stashing.m */,
BDD8AE6D13131B8800CB5D40 /* GTEnumerator.h */,
BDD8AE6E13131B8800CB5D40 /* GTEnumerator.m */,
BD6C22A71314625800992935 /* GTObject.h */,
Expand Down Expand Up @@ -910,6 +921,7 @@
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;
Expand Down Expand Up @@ -946,6 +958,7 @@
3011D8711668E78500CE3409 /* GTDiffHunk.h in Headers */,
30FDC07F16835A8100654BF0 /* GTDiffLine.h in Headers */,
3011D8771668F29600CE3409 /* GTDiffDelta.h in Headers */,
D015F7CA17F695E800AD5E1F /* GTRepository+Stashing.h in Headers */,
882154691714740500D76B76 /* GTReflog.h in Headers */,
8821547D17147B3600D76B76 /* GTOID.h in Headers */,
8821547617147A5200D76B76 /* GTReflogEntry.h in Headers */,
Expand Down Expand Up @@ -1248,6 +1261,7 @@
8821547917147A5200D76B76 /* GTReflogEntry.m in Sources */,
8821548017147B3600D76B76 /* GTOID.m in Sources */,
5BE6128B1745EE3400266D8C /* GTTreeBuilder.m in Sources */,
D015F7CD17F695E800AD5E1F /* GTRepository+Stashing.m in Sources */,
D09C2E391755F16200065E36 /* GTSubmodule.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -1268,6 +1282,7 @@
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 */,
30B1E8001703871900D0814D /* GTTimeAdditionsSpec.m in Sources */,
88C0BC5917038CF3009E99AA /* GTConfigurationSpec.m in Sources */,
Expand Down Expand Up @@ -1315,6 +1330,7 @@
3011D8731668E78500CE3409 /* GTDiffHunk.m in Sources */,
3011D8791668F29600CE3409 /* GTDiffDelta.m in Sources */,
30FDC08116835A8100654BF0 /* GTDiffLine.m in Sources */,
D015F7CC17F695E800AD5E1F /* GTRepository+Stashing.m in Sources */,
30B1E7F01703522100D0814D /* NSDate+GTTimeAdditions.m in Sources */,
8821546B1714740500D76B76 /* GTReflog.m in Sources */,
8821547817147A5200D76B76 /* GTReflogEntry.m in Sources */,
Expand Down
119 changes: 119 additions & 0 deletions ObjectiveGitTests/GTRepositoryStashingSpec.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// GTRepositoryStashingSpec.m
// ObjectiveGitFramework
//
// Created by Justin Spahr-Summers on 2013-09-27.
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
//

#import "GTRepository+Stashing.h"

SpecBegin(GTRepositoryStashing)

__block GTRepository *repository;

beforeEach(^{
repository = [self fixtureRepositoryNamed:@"Test_App"];
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(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", ^{
NSURL *fileURL = [repository.fileURL URLByAppendingPathComponent:@"README.md"];
NSString *newContent = @"foobar";

NSString *oldContent = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL];
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);

NSError *error = nil;
GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error];
expect(stash).notTo.beNil();
expect(error).to.beNil();

expect([NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL]).to.equal(oldContent);
});

it(@"should create a stash with uncommitted changes", ^{
NSURL *fileURL = [repository.fileURL URLByAppendingPathComponent:@"README.md"];
NSString *newContent = @"foobar";

NSString *oldContent = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL];
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);

NSError *error = nil;
GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error];
expect(stash).notTo.beNil();
expect(error).to.beNil();

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();

NSError *error = nil;
GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagDefault error:&error];
expect(stash).to.beNil();

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();

NSError *error = nil;
GTCommit *stash = [repository stashChangesWithMessage:nil flags:GTRepositoryStashFlagIncludeUntracked error:&error];
expect(stash).notTo.beNil();
expect(error).to.beNil();
});

it(@"should enumerate stashes", ^{
const int stashCount = 3;
NSMutableArray *stashCommits = [NSMutableArray arrayWithCapacity:stashCount];

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();

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();

[stashCommits insertObject:stash atIndex:0];
}

__block NSUInteger lastIndex = 0;
[repository enumerateStashesUsingBlock:^(NSUInteger i, NSString *message, GTOID *oid, BOOL *stop) {
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);

if (i == 2) *stop = YES;
}];

expect(lastIndex).to.equal(2);
});

SpecEnd