diff --git a/External/libgit2 b/External/libgit2 index bdf0e7345..a01d3a0b5 160000 --- a/External/libgit2 +++ b/External/libgit2 @@ -1 +1 @@ -Subproject commit bdf0e734506b5b18234d48a0e7c6995aeda30b9d +Subproject commit a01d3a0b503559fe09519b787f353b25e52a0420 diff --git a/ObjectiveGit/GTIndex.h b/ObjectiveGit/GTIndex.h index b649e3455..4119c8917 100644 --- a/ObjectiveGit/GTIndex.h +++ b/ObjectiveGit/GTIndex.h @@ -135,6 +135,14 @@ /// 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. +/// name - The name of the entry to add. Cannot be nil. +/// error - The error if one occurred. +- (BOOL)addData:(NSData *)data withName:(NSString *)name 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. diff --git a/ObjectiveGit/GTIndex.m b/ObjectiveGit/GTIndex.m index 940980f14..dc3f23dcd 100644 --- a/ObjectiveGit/GTIndex.m +++ b/ObjectiveGit/GTIndex.m @@ -36,6 +36,7 @@ #import "GTRepository+Private.h" #import "GTRepository.h" #import "GTTree.h" +#import "GTBlob.h" #import "NSArray+StringArray.h" #import "NSError+Git.h" @@ -76,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"]; @@ -141,7 +143,7 @@ - (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 { @@ -180,6 +182,25 @@ - (BOOL)addFile:(NSString *)file error:(NSError **)error { return YES; } +- (BOOL)addData:(NSData *)data withName:(NSString *)name error:(NSError **)error { + NSParameterAssert(data != nil); + NSParameterAssert(name != nil); + + git_index_entry entry; + memset(&entry, 0x0, sizeof(git_index_entry)); + entry.path = [name 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.", name]; + return NO; + } + + return YES; +} + - (BOOL)addContentsOfTree:(GTTree *)tree error:(NSError **)error { NSParameterAssert(tree != nil); diff --git a/ObjectiveGit/GTIndexEntry.h b/ObjectiveGit/GTIndexEntry.h index bba4a7436..8f2bc45ab 100644 --- a/ObjectiveGit/GTIndexEntry.h +++ b/ObjectiveGit/GTIndexEntry.h @@ -29,6 +29,9 @@ #import #include "git2/index.h" +#import "GTObject.h" + +@class GTIndex; typedef NS_ENUM(NSInteger, GTIndexEntryStatus) { GTIndexEntryStatusUpdated = 0, @@ -40,6 +43,22 @@ typedef NS_ENUM(NSInteger, GTIndexEntryStatus) { @interface GTIndexEntry : NSObject +/// 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 *)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 *index; + /// The repository-relative path for the entry. @property (nonatomic, readonly, copy) NSString *path; @@ -49,14 +68,21 @@ typedef NS_ENUM(NSInteger, GTIndexEntryStatus) { /// What is the entry's status? @property (nonatomic, readonly) GTIndexEntryStatus status; -/// Initializes the receiver with the given libgit2 index entry. +/// The OID of the entry. +@property (nonatomic, strong, readonly) GTOID *OID; + +/// Convert the entry into an GTObject /// -/// entry - The libgit2 index entry. Cannot be NULL. +/// error - will be filled if an error occurs /// -/// Returns the initialized object. -- (id)initWithGitIndexEntry:(const git_index_entry *)entry NS_DESIGNATED_INITIALIZER; +/// Returns this entry as a GTObject or nil if an error occurred. +- (GTObject *)GTObject:(NSError **)error; -/// The underlying `git_index_entry` object. -- (const git_index_entry *)git_index_entry __attribute__((objc_returns_inner_pointer)); +@end + +@interface GTObject (GTIndexEntry) + ++ (instancetype)objectWithIndexEntry:(GTIndexEntry *)treeEntry error:(NSError **)error; +- (instancetype)initWithIndexEntry:(GTIndexEntry *)treeEntry error:(NSError **)error; @end diff --git a/ObjectiveGit/GTIndexEntry.m b/ObjectiveGit/GTIndexEntry.m index 0360755da..8a6971c70 100644 --- a/ObjectiveGit/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,22 +45,27 @@ @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)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 { @@ -82,8 +92,45 @@ - (GTIndexEntryStatus)status { } else if ((self.flags & GIT_IDXENTRY_REMOVE) != 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_otype)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/ObjectiveGitTests/GTIndexSpec.m b/ObjectiveGitTests/GTIndexSpec.m index 9a93d88d2..c065e359a 100644 --- a/ObjectiveGitTests/GTIndexSpec.m +++ b/ObjectiveGitTests/GTIndexSpec.m @@ -276,6 +276,31 @@ }); }); +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 withName:@"bar/foo" error:&error]; + expect(error).to(beNil()); + + GTIndexEntry *entry = [index entryWithName:@"bar/foo" error:&error]; + expect(entry).notTo(beNil()); + expect(error).to(beNil()); + }); +}); + afterEach(^{ [self tearDown]; });