Skip to content

Commit 47dc0fc

Browse files
committed
Git notes support.
Added `GTNote` class and a few helpers into `GTRepository`.
1 parent 0871b0e commit 47dc0fc

File tree

7 files changed

+361
-0
lines changed

7 files changed

+361
-0
lines changed

ObjectiveGit/GTNote.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// GTNote.h
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Slava Karpenko on 5/16/2016.
6+
//
7+
// The MIT License
8+
//
9+
// Copyright (c) 2016 Wildbit LLC
10+
//
11+
// Permission is hereby granted, free of charge, to any person obtaining a copy
12+
// of this software and associated documentation files (the "Software"), to deal
13+
// in the Software without restriction, including without limitation the rights
14+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15+
// copies of the Software, and to permit persons to whom the Software is
16+
// furnished to do so, subject to the following conditions:
17+
//
18+
// The above copyright notice and this permission notice shall be included in
19+
// all copies or substantial portions of the Software.
20+
//
21+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27+
// THE SOFTWARE.
28+
//
29+
30+
#import <Foundation/Foundation.h>
31+
#import "git2/oid.h"
32+
33+
@class GTSignature;
34+
@class GTRepository;
35+
@class GTOID;
36+
@class GTObject;
37+
38+
NS_ASSUME_NONNULL_BEGIN
39+
40+
@interface GTNote : NSObject {}
41+
42+
/// The author of the note.
43+
@property (nonatomic, readonly, strong, nullable) GTSignature *author;
44+
45+
/// The committer of the note.
46+
@property (nonatomic, readonly, strong, nullable) GTSignature *committer;
47+
48+
/// Content of the note.
49+
@property (nonatomic, readonly, strong) NSString *note;
50+
51+
@property (nonatomic, readonly, strong) GTObject *target;
52+
53+
/// The underlying `git_note` object.
54+
- (git_note * _Nullable)git_note __attribute__((objc_returns_inner_pointer));
55+
56+
/// These initializers may fail if there's no note attached to the provided oid.
57+
- (nullable instancetype)initWithTargetOID:(GTOID*)oid repository:(GTRepository*)repository ref:(nullable NSString*)ref;
58+
- (nullable instancetype)initWithTargetGitOID:(git_oid*)oid repository:(git_repository *)repository ref:(const char* _Nullable)ref;
59+
60+
@end
61+
62+
NS_ASSUME_NONNULL_END
63+

ObjectiveGit/GTNote.m

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// GTNote.m
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Slava Karpenko on 16.05.16.
6+
// Copyright © 2016 Wildbit LLC. All rights reserved.
7+
//
8+
9+
#import "GTNote.h"
10+
#import "NSError+Git.h"
11+
#import "GTSignature.h"
12+
#import "GTReference.h"
13+
#import "GTRepository.h"
14+
#import "NSString+Git.h"
15+
#import "GTOID.h"
16+
17+
#import "git2/errors.h"
18+
#import "git2/notes.h"
19+
20+
@interface GTNote ()
21+
{
22+
git_note *_note;
23+
}
24+
25+
@end
26+
@implementation GTNote
27+
28+
- (NSString *)description {
29+
return [NSString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self];
30+
}
31+
32+
#pragma mark API
33+
34+
- (void)dealloc {
35+
if (_note != NULL) {
36+
git_note_free(_note);
37+
}
38+
}
39+
40+
- (git_note *)git_note {
41+
return _note;
42+
}
43+
44+
- (NSString*)note {
45+
return @(git_note_message(self.git_note));
46+
}
47+
48+
- (GTSignature *)author {
49+
return [[GTSignature alloc] initWithGitSignature:git_note_author(self.git_note)];
50+
}
51+
52+
- (GTSignature *)committer {
53+
return [[GTSignature alloc] initWithGitSignature:git_note_committer(self.git_note)];
54+
}
55+
56+
- (GTOID*)targetOID {
57+
return [GTOID oidWithGitOid:git_note_id(self.git_note)];
58+
}
59+
60+
- (instancetype)initWithTargetOID:(GTOID*)oid repository:(GTRepository*)repository ref:(NSString*)ref {
61+
return [self initWithTargetGitOID:(git_oid *)oid.git_oid repository:repository.git_repository ref:ref.UTF8String];
62+
}
63+
64+
- (instancetype)initWithTargetGitOID:(git_oid*)oid repository:(git_repository *)repository ref:(const char*)ref {
65+
if (self = [super init]) {
66+
int gitErr = git_note_read(&_note, repository, ref, oid);
67+
68+
if (gitErr != GIT_OK)
69+
return nil; // Cannot read the note, means it either doesn't exists for this object, this object is not found, or whatever else.
70+
}
71+
72+
return self;
73+
}
74+
75+
@end

ObjectiveGit/GTRepository.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
@class GTTag;
5252
@class GTTree;
5353
@class GTRemote;
54+
@class GTNote;
5455

5556
NS_ASSUME_NONNULL_BEGIN
5657

@@ -604,6 +605,48 @@ typedef NS_ENUM(NSInteger, GTRepositoryStateType) {
604605
/// Returns YES if operation was successful, NO otherwise
605606
- (BOOL)cleanupStateWithError:(NSError **)error;
606607

608+
/// Creates a new note in this repo (using a default notes reference, e.g. "refs/notes/commits")
609+
///
610+
/// note - Note text.
611+
/// theTarget - Object (usually a commit) to which this note attaches to.
612+
/// This object must belong to this repository.
613+
/// ref - Name for the notes reference in the repo, or nil for default ("refs/notes/commits")
614+
/// author - Signature of the author for this note, and
615+
/// of the note creation time
616+
/// committer - Signature of the committer for this note.
617+
/// overwrite - If set to YES, the note will be overwritten if it already exists.
618+
/// error - Will be filled with a NSError object in case of error.
619+
/// May be NULL.
620+
///
621+
/// Returns the newly created note or nil on error.
622+
- (nullable GTNote *)createNote:(NSString *)note target:(GTObject *)theTarget ref:(nullable NSString *)ref author:(GTSignature *)author committer:(GTSignature *)committer overwriteIfExists:(BOOL)overwrite error:(NSError **)error;
623+
624+
/// Removes a note attached to object in this repo (using a default notes reference, e.g. "refs/notes/commits")
625+
///
626+
/// parentObject - Object (usually a commit) to which the note to be removed is attached to.
627+
/// This object must belong to this repository.
628+
/// ref - Name for the notes reference in the repo, or nil for default ("refs/notes/commits")
629+
/// author - Signature of the author for this note removal, and
630+
/// of the note removal time
631+
/// committer - Signature of the committer for this note removal.
632+
/// error - Will be filled with a NSError object in case of error.
633+
/// May be NULL.
634+
///
635+
/// Returns the YES on success and NO on error.
636+
- (BOOL)removeNoteFromObject:(GTObject *)parentObject ref:(nullable NSString *)ref author:(GTSignature *)author committer:(GTSignature *)committer error:(NSError **)error;
637+
638+
/// Enumerates through all stored notes in this repo (using a default notes reference, e.g. "refs/notes/commits")
639+
///
640+
/// error - Will be filled with a NSError object in case of error.
641+
/// May be null.
642+
/// ref - Name for the notes reference in the repo, or nil for default ("refs/notes/commits")
643+
/// block - A block to be called on each encountered note object. The block accepts
644+
/// a reference to `note`, an `object` that is annotated with the note.
645+
/// If the block sets `stop` to YES, the iterator is finished.
646+
///
647+
/// Returns YES on overall success or NO on error of any kind.
648+
- (BOOL)enumerateNotesWithError:(NSError **)error ref:(nullable NSString *)ref usingBlock:(void (^)(GTNote *note, GTObject *object, BOOL *stop))block;
649+
607650
@end
608651

609652
NS_ASSUME_NONNULL_END

ObjectiveGit/GTRepository.m

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#import "NSError+Git.h"
5353
#import "NSString+Git.h"
5454
#import "GTRepository+References.h"
55+
#import "GTNote.h"
5556

5657
#import "git2.h"
5758

@@ -919,4 +920,76 @@ - (BOOL)cleanupStateWithError:(NSError * _Nullable __autoreleasing *)error {
919920
return YES;
920921
}
921922

923+
#pragma mark Notes
924+
925+
- (GTNote *)createNote:(NSString *)note target:(GTObject *)theTarget ref:(NSString *)ref author:(GTSignature *)author committer:(GTSignature *)committer overwriteIfExists:(BOOL)overwrite error:(NSError **)error {
926+
git_oid oid;
927+
928+
int gitError = git_note_create(&oid, self.git_repository, ref.UTF8String, author.git_signature, committer.git_signature, theTarget.OID.git_oid, [note UTF8String], overwrite ? 1 : 0);
929+
if (gitError != GIT_OK) {
930+
if (error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to create a note in repository"];
931+
932+
return nil;
933+
}
934+
935+
return [[GTNote alloc] initWithTargetOID:theTarget.OID repository:self ref:ref];
936+
}
937+
938+
- (BOOL)removeNoteFromObject:(GTObject *)parentObject ref:(NSString *)ref author:(GTSignature *)author committer:(GTSignature *)committer error:(NSError **)error {
939+
int gitError = git_note_remove(self.git_repository, ref.UTF8String, author.git_signature, committer.git_signature, parentObject.OID.git_oid);
940+
if (gitError != GIT_OK) {
941+
if(error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to delete note from %@", parentObject];
942+
return NO;
943+
}
944+
945+
return YES;
946+
}
947+
948+
- (BOOL)enumerateNotesWithError:(NSError **)error ref:(NSString *)ref usingBlock:(void (^)(GTNote *note, GTObject *object, BOOL *stop))block
949+
{
950+
git_note_iterator* iter = NULL;
951+
952+
int gitError = git_note_iterator_new(&iter, self.git_repository, ref.UTF8String);
953+
954+
if (gitError != GIT_OK)
955+
{
956+
if(error != NULL) *error = [NSError git_errorFor:gitError description:@"Failed to enumerate notes"];
957+
return NO;
958+
}
959+
960+
git_oid note_id;
961+
git_oid object_id;
962+
BOOL success = YES;
963+
964+
while (git_note_next(&note_id, &object_id, iter) == GIT_OK) {
965+
NSError* lookupErr = nil;
966+
967+
GTNote* note = [[GTNote alloc] initWithTargetGitOID:&object_id repository:self.git_repository ref:ref.UTF8String];
968+
if (note == nil) {
969+
if (error != NULL)
970+
*error = [NSError git_errorFor:GIT_ENOTFOUND description:@"Cannot create note"];
971+
success = NO;
972+
break;
973+
}
974+
975+
GTObject* obj = [self lookUpObjectByGitOid:&object_id error:&lookupErr];
976+
if (obj == nil && lookupErr != nil) {
977+
if (error != NULL)
978+
*error = lookupErr;
979+
success = NO;
980+
break;
981+
}
982+
983+
BOOL stop = NO;
984+
block(note, obj, &stop);
985+
if (stop) {
986+
break;
987+
}
988+
}
989+
990+
git_note_iterator_free(iter);
991+
992+
return success;
993+
}
994+
922995
@end

ObjectiveGit/ObjectiveGit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ FOUNDATION_EXPORT const unsigned char ObjectiveGitVersionString[];
7070
#import <ObjectiveGit/GTFilterList.h>
7171
#import <ObjectiveGit/GTFilterSource.h>
7272
#import <ObjectiveGit/GTFetchHeadEntry.h>
73+
#import <ObjectiveGit/GTNote.h>
7374

7475
#import <ObjectiveGit/GTObjectDatabase.h>
7576
#import <ObjectiveGit/GTOdbObject.h>

ObjectiveGitFramework.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,13 @@
368368
F8E4A2911A170CA6006485A8 /* GTRemotePushSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F8E4A2901A170CA6006485A8 /* GTRemotePushSpec.m */; };
369369
F8E68E731C800B1600DBC517 /* ZipArchive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E68E721C800B1600DBC517 /* ZipArchive.framework */; };
370370
F8EFA03A1B4059ED000FF7D0 /* GTUtilityFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = F8EFA0391B4059ED000FF7D0 /* GTUtilityFunctions.m */; };
371+
F964D5F11CE9D9B200F1D8DD /* GTNote.h in Headers */ = {isa = PBXBuildFile; fileRef = F964D5EF1CE9D9B200F1D8DD /* GTNote.h */; settings = {ATTRIBUTES = (Public, ); }; };
372+
F964D5F21CE9D9B200F1D8DD /* GTNote.h in Headers */ = {isa = PBXBuildFile; fileRef = F964D5EF1CE9D9B200F1D8DD /* GTNote.h */; settings = {ATTRIBUTES = (Public, ); }; };
373+
F964D5F31CE9D9B200F1D8DD /* GTNote.m in Sources */ = {isa = PBXBuildFile; fileRef = F964D5F01CE9D9B200F1D8DD /* GTNote.m */; };
374+
F964D5F51CE9D9B200F1D8DD /* GTNote.m in Sources */ = {isa = PBXBuildFile; fileRef = F964D5F01CE9D9B200F1D8DD /* GTNote.m */; };
375+
F9D1D4231CEB79D1009E5855 /* GTNoteSpec.m in Resources */ = {isa = PBXBuildFile; fileRef = F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */; };
376+
F9D1D4241CEB79D1009E5855 /* GTNoteSpec.m in Resources */ = {isa = PBXBuildFile; fileRef = F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */; };
377+
F9D1D4251CEB7BA6009E5855 /* GTNoteSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */; };
371378
/* End PBXBuildFile section */
372379

373380
/* Begin PBXContainerItemProxy section */
@@ -644,6 +651,9 @@
644651
F8EFA0361B405020000FF7D0 /* GTRepository+PullSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+PullSpec.m"; sourceTree = "<group>"; };
645652
F8EFA0381B4059ED000FF7D0 /* GTUtilityFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTUtilityFunctions.h; sourceTree = "<group>"; };
646653
F8EFA0391B4059ED000FF7D0 /* GTUtilityFunctions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTUtilityFunctions.m; sourceTree = "<group>"; };
654+
F964D5EF1CE9D9B200F1D8DD /* GTNote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTNote.h; sourceTree = "<group>"; };
655+
F964D5F01CE9D9B200F1D8DD /* GTNote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTNote.m; sourceTree = "<group>"; };
656+
F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTNoteSpec.m; sourceTree = "<group>"; };
647657
/* End PBXFileReference section */
648658

649659
/* Begin PBXFrameworksBuildPhase section */
@@ -823,6 +833,7 @@
823833
30A269AC17B4878C000FE64E /* GTRepository+StatusSpec.m */,
824834
88E353051982EA6B0051001F /* GTRepositoryAttributesSpec.m */,
825835
4D12323F178E009E0048F785 /* GTRepositoryCommittingSpec.m */,
836+
F9D1D4221CEB79D1009E5855 /* GTNoteSpec.m */,
826837
88234B2518F2FE260039972E /* GTRepositoryResetSpec.m */,
827838
D0AC906B172F941F00347DC4 /* GTRepositorySpec.m */,
828839
D015F7D417F6965400AD5E1F /* GTRepositoryStashingSpec.m */,
@@ -899,6 +910,8 @@
899910
BD6B0416131496CC001909D0 /* GTTreeEntry.m */,
900911
5BE612861745EE3300266D8C /* GTTreeBuilder.h */,
901912
5BE612871745EE3300266D8C /* GTTreeBuilder.m */,
913+
F964D5EF1CE9D9B200F1D8DD /* GTNote.h */,
914+
F964D5F01CE9D9B200F1D8DD /* GTNote.m */,
902915
BDD62922131C03D600DE34D1 /* GTTag.h */,
903916
BDD62923131C03D600DE34D1 /* GTTag.m */,
904917
BDFAF9C1131C1845000508BC /* GTIndex.h */,
@@ -1104,6 +1117,7 @@
11041117
88746CC417FA1C950005888A /* GTRepository+Committing.h in Headers */,
11051118
D09C2E361755F16200065E36 /* GTSubmodule.h in Headers */,
11061119
F8D1BDEE1B31FE7C00CDEC90 /* GTRepository+Pull.h in Headers */,
1120+
F964D5F11CE9D9B200F1D8DD /* GTNote.h in Headers */,
11071121
4D79C0EE17DF9F4D00997DE4 /* GTCredential.h in Headers */,
11081122
);
11091123
runOnlyForDeploymentPostprocessing = 0;
@@ -1162,6 +1176,7 @@
11621176
D01B6F7119F82FB300D411BC /* GTDiffDelta.h in Headers */,
11631177
D01B6F3B19F82F8700D411BC /* GTTreeBuilder.h in Headers */,
11641178
D01B6F1B19F82F7B00D411BC /* NSDate+GTTimeAdditions.h in Headers */,
1179+
F964D5F21CE9D9B200F1D8DD /* GTNote.h in Headers */,
11651180
D01B6F6319F82FA600D411BC /* GTFilterList.h in Headers */,
11661181
889923FB19FF5DD40092A9A6 /* git2 in Headers */,
11671182
F8D1BDEF1B31FE7C00CDEC90 /* GTRepository+Pull.h in Headers */,
@@ -1300,6 +1315,7 @@
13001315
isa = PBXResourcesBuildPhase;
13011316
buildActionMask = 2147483647;
13021317
files = (
1318+
F9D1D4231CEB79D1009E5855 /* GTNoteSpec.m in Resources */,
13031319
D09C2E51175602A500065E36 /* fixtures.zip in Resources */,
13041320
);
13051321
runOnlyForDeploymentPostprocessing = 0;
@@ -1316,6 +1332,7 @@
13161332
isa = PBXResourcesBuildPhase;
13171333
buildActionMask = 2147483647;
13181334
files = (
1335+
F9D1D4241CEB79D1009E5855 /* GTNoteSpec.m in Resources */,
13191336
F8D007A81B4FA045009A8DAF /* fixtures.zip in Resources */,
13201337
);
13211338
runOnlyForDeploymentPostprocessing = 0;
@@ -1386,6 +1403,7 @@
13861403
isa = PBXSourcesBuildPhase;
13871404
buildActionMask = 2147483647;
13881405
files = (
1406+
F9D1D4251CEB7BA6009E5855 /* GTNoteSpec.m in Sources */,
13891407
23BB67C11C7DF60300A37A66 /* GTRepository+PullSpec.m in Sources */,
13901408
D0751CD918BE520400134314 /* GTFilterListSpec.m in Sources */,
13911409
200578C518932A82001C06C3 /* GTBlameSpec.m in Sources */,
@@ -1475,6 +1493,7 @@
14751493
88746CC617FA1C950005888A /* GTRepository+Committing.m in Sources */,
14761494
D015F7CC17F695E800AD5E1F /* GTRepository+Stashing.m in Sources */,
14771495
30B1E7F01703522100D0814D /* NSDate+GTTimeAdditions.m in Sources */,
1496+
F964D5F31CE9D9B200F1D8DD /* GTNote.m in Sources */,
14781497
8821546B1714740500D76B76 /* GTReflog.m in Sources */,
14791498
8821547817147A5200D76B76 /* GTReflogEntry.m in Sources */,
14801499
8821547F17147B3600D76B76 /* GTOID.m in Sources */,
@@ -1535,6 +1554,7 @@
15351554
D01B6F5419F82FA600D411BC /* GTBlameHunk.m in Sources */,
15361555
D01B6F6819F82FA600D411BC /* GTFetchHeadEntry.m in Sources */,
15371556
D01B6F3619F82F8700D411BC /* GTBlob.m in Sources */,
1557+
F964D5F51CE9D9B200F1D8DD /* GTNote.m in Sources */,
15381558
D01B6F6E19F82FB300D411BC /* GTDiffFile.m in Sources */,
15391559
D01B6F5619F82FA600D411BC /* GTReflog.m in Sources */,
15401560
D01B6F5E19F82FA600D411BC /* GTCredential.m in Sources */,

0 commit comments

Comments
 (0)