Skip to content
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

Git notes #576

Merged
merged 23 commits into from
Dec 5, 2016
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
abc1d74
Updating libgit2 pointer
slavikus Mar 23, 2016
41279e4
Updating for new libgit2 git_remote_connect() call
slavikus Mar 23, 2016
0871b0e
Merge remote-tracking branch 'upstream/master'
slavikus May 4, 2016
47dc0fc
Git notes support.
slavikus May 19, 2016
fe13986
Added support for [GTRepository pushNotes:...]
slavikus May 19, 2016
6df9faf
Fixes per @tiennou feedback
slavikus May 20, 2016
947058d
Push branches and notes in one operation
slavikus May 23, 2016
e99a62e
Check if notes reference exists before pushing it
slavikus May 26, 2016
b64f024
Merge remote-tracking branch 'upstream/master'
slavikus Jul 15, 2016
0661bab
Merge branch 'master' into git-notes
slavikus Jul 15, 2016
c27e838
Merge remote-tracking branch 'upstream/master' into git-notes
slavikus Sep 6, 2016
73037a4
Style fixes per @tiennou feedback
slavikus Sep 6, 2016
75e5356
Merge remote-tracking branch 'upstream/master' into git-notes
slavikus Nov 8, 2016
16ef587
Merge remote-tracking branch 'upstream/master'
slavikus Nov 23, 2016
2352543
Respect nullable returns in various methods
slavikus Nov 23, 2016
6e96e11
Merge branch 'master' into git-notes
slavikus Nov 23, 2016
6228da7
Merge remote-tracking branch 'upstream/master' into git-notes
slavikus Nov 30, 2016
2fea0a5
Fix nullability warning for GTNote
slavikus Dec 2, 2016
5bf9d5f
Corrections per @tiennou feedback
slavikus Dec 2, 2016
f4a1103
Rollback libgit2 back to globally used release
slavikus Dec 2, 2016
55898a7
More fixes for pull request #576
slavikus Dec 2, 2016
16f980f
Extra newlines removal
slavikus Dec 2, 2016
927cc49
Fix tests for GTNote
slavikus Dec 4, 2016
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
2 changes: 1 addition & 1 deletion External/libgit2
Submodule libgit2 updated 54 files
+0 −3 .travis.yml
+24 −1 CHANGELOG.md
+1 −1 CMakeLists.txt
+16 −1 CONTRIBUTING.md
+1 −6 appveyor.yml
+2 −3 examples/general.c
+43 −0 include/git2/blob.h
+67 −0 include/git2/commit.h
+0 −8 include/git2/common.h
+10 −1 include/git2/merge.h
+47 −1 include/git2/odb.h
+177 −0 include/git2/sys/merge.h
+1 −3 script/appveyor-mingw.sh
+0 −13 script/toolchain-mingw32.cmake
+71 −39 src/blob.c
+192 −47 src/commit.c
+39 −16 src/config_file.c
+6 −1 src/filebuf.c
+1 −0 src/filebuf.h
+2 −0 src/global.c
+3 −11 src/ignore.c
+1 −0 src/indexer.c
+160 −63 src/merge.c
+61 −2 src/merge.h
+396 −0 src/merge_driver.c
+60 −0 src/merge_driver.h
+11 −49 src/merge_file.c
+187 −54 src/odb.c
+3 −0 src/pack.c
+0 −3 src/refs.c
+17 −61 src/transports/smart_protocol.c
+0 −2 src/tree.c
+1 −2 src/xdiff/xprepare.c
+0 −13 tests/attr/ignore.c
+139 −0 tests/commit/write.c
+1 −1 tests/config/multivar.c
+0 −27 tests/config/write.c
+0 −2 tests/core/array.c
+2 −6 tests/core/stream.c
+51 −9 tests/index/nsec.c
+388 −0 tests/merge/driver.c
+2 −2 tests/merge/workdir/dirty.c
+36 −0 tests/merge/workdir/simple.c
+0 −156 tests/object/blob/fromchunks.c
+103 −0 tests/object/blob/fromstream.c
+155 −0 tests/odb/mixed.c
+26 −5 tests/rebase/abort.c
+57 −0 tests/rebase/merge.c
+205 −0 tests/rebase/setup.c
+5 −5 tests/refs/create.c
+0 −8 tests/refs/lookup.c
+0 −5 tests/repo/iterator.c
+3 −3 tests/reset/hard.c
+1 −4 tests/status/worktree.c
93 changes: 93 additions & 0 deletions ObjectiveGit/GTNote.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// GTNote.h
// ObjectiveGitFramework
//
// Created by Slava Karpenko on 5/16/2016.
//
// The MIT License
//
// Copyright (c) 2016 Wildbit LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#import <Foundation/Foundation.h>
#import "git2/oid.h"

@class GTSignature;
@class GTRepository;
@class GTOID;
@class GTObject;

NS_ASSUME_NONNULL_BEGIN

@interface GTNote : NSObject {}

/// The author of the note.
@property (nonatomic, readonly, strong, nullable) GTSignature *author;

/// The committer of the note.
@property (nonatomic, readonly, strong, nullable) GTSignature *committer;

/// Content of the note.
@property (nonatomic, readonly, strong) NSString *note;

@property (nonatomic, readonly, strong) GTObject *target;

/// The underlying `git_note` object.
- (git_note *)git_note __attribute__((objc_returns_inner_pointer));

/// Create a note with target OID in the given repository.
///
/// oid - OID of the target to attach to
/// repository - Repository containing the target OID refers to
/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits")
/// error - Will be filled with a NSError object in case of error.
/// May be NULL.
///
/// Returns initialized GTNote instance or nil on failure (error will be populated, if passed).
- (nullable instancetype)initWithTargetOID:(GTOID *)oid repository:(GTRepository *)repository referenceName:(nullable NSString *)referenceName error:(NSError **)error;

/// Create a note with target libgit2 oid in the given repository.
///
/// oid - git_oid of the target to attach to
/// repository - Repository containing the target OID refers to
/// referenceName - Name for the notes reference in the repo, or NULL for default ("refs/notes/commits")
/// error - Will be filled with a git error code in case of error.
/// May be NULL.
///
/// Returns initialized GTNote instance or nil on failure (error will be populated, if passed).
- (nullable instancetype)initWithTargetGitOID:(git_oid *)oid repository:(git_repository *)repository referenceName:(const char * _Nullable)referenceName error:(int * _Nullable)error;

- (instancetype)init NS_UNAVAILABLE;


/// Return a default reference name (that is used if you pass nil to any referenceName parameter)
///
/// repository - Repository for which to get the default notes reference name.
/// error - Will be filled with a git error code in case of error.
/// May be NULL.
///
/// Returns default reference name (usually "refs/notes/commits").
+ (NSString *)defaultReferenceNameForRepository:(GTRepository *)repository error:(NSError **)error;

@end

NS_ASSUME_NONNULL_END

119 changes: 119 additions & 0 deletions ObjectiveGit/GTNote.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// GTNote.m
// ObjectiveGitFramework
//
// Created by Slava Karpenko on 16.05.16.
// Copyright © 2016 Wildbit LLC. All rights reserved.
//

#import "GTNote.h"
#import "NSError+Git.h"
#import "GTSignature.h"
#import "GTReference.h"
#import "GTRepository.h"
#import "NSString+Git.h"
#import "GTOID.h"

#import "git2/errors.h"
#import "git2/notes.h"

@interface GTNote ()
{
git_note *_note;
}

@end
@implementation GTNote

- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self];
}

#pragma mark API

- (void)dealloc {
if (_note != NULL) {
git_note_free(_note);
}
}

- (git_note *)git_note {
return _note;
}

- (NSString *)note {
return @(git_note_message(self.git_note));
}

- (GTSignature *)author {
return [[GTSignature alloc] initWithGitSignature:git_note_author(self.git_note)];
}

- (GTSignature *)committer {
return [[GTSignature alloc] initWithGitSignature:git_note_committer(self.git_note)];
}

- (GTOID *)targetOID {
return [GTOID oidWithGitOid:git_note_id(self.git_note)];
}

- (instancetype)initWithTargetOID:(GTOID *)oid repository:(GTRepository *)repository referenceName:(NSString *)referenceName error:(NSError **)error {
int err = GIT_OK;

id object = [self initWithTargetGitOID:(git_oid *)oid.git_oid repository:repository.git_repository referenceName:referenceName.UTF8String error:&err];

if (err != GIT_OK && error != NULL) {
*error = [NSError git_errorFor:err description:@"Failed to create a note."];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message says "create" but method works as "read".

}

return object;
}

- (instancetype)initWithTargetGitOID:(git_oid *)oid repository:(git_repository *)repository referenceName:(const char *)referenceName error:(int *)error {
self = [super init];
if (self == nil) return nil;

int gitErr = git_note_read(&_note, repository, referenceName, oid);

if (gitErr != GIT_OK) {
if (error != NULL) *error = gitErr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I do not think it works that way 😉.

Actually, it does.

Can you make it less "unexpected" ? As in, not using int *.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the error altogether. In all other parts of the framework initializers that take libgit2 pointers never return the error, only nil on failure.


return nil;
}

return self;
}

- (instancetype)init {
NSAssert(NO, @"Call to an unavailable initializer.");
return nil;
}

+ (NSString *)defaultReferenceNameForRepository:(GTRepository *)repository error:(NSError **)error {
NSString *noteRef = nil;

git_buf default_ref_name = { 0 };
int gitErr = git_note_default_ref(&default_ref_name, repository.git_repository);
if (gitErr != GIT_OK) {
git_buf_free(&default_ref_name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you can get both an error and a pointer to free ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who knows? After all, if it failed filling a pointer, git_buf_free would be a no-op, so it may be redundant, but definitely there's no harm in doing that. Besides, it's a pattern I picked from other parts of the code (see, for example, GTRepository.m:604 in - (NSString *)preparedMessageWithError:(NSError **)error.


if (error != NULL) {
*error = [NSError git_errorFor:gitErr description:@"Unable to get default git notes reference name"];
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: whitespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm blind. What's wrong with the whitespace above? Triple checked and don't see anything wrong. :\

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant the newline is unwarranted.

return nil;
}

if (default_ref_name.ptr != NULL) {
noteRef = @(default_ref_name.ptr);
} else {
if (error != NULL) {
*error = [NSError git_errorFor:GIT_ERROR description:@"Unable to get default git notes reference name"];
}
}

git_buf_free(&default_ref_name);

return noteRef;
}
@end
34 changes: 34 additions & 0 deletions ObjectiveGit/GTRepository+RemoteOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,40 @@ typedef NS_ENUM(NSInteger, GTFetchPruneOption) {
/// will point to an error describing what happened).
- (BOOL)pushBranches:(NSArray<GTBranch *> *)branches toRemote:(GTRemote *)remote withOptions:(nullable NSDictionary *)options error:(NSError **)error progress:(nullable void (^)(unsigned int current, unsigned int total, size_t bytes, BOOL *stop))progressBlock;

/// Push an array of branches to a remote, together with notes (in one push).
/// Normally, if you use Git notes functionality, to push the notes to remote, you would have to
/// do two operations: pushBranches followed by pushNotes. This is unrational, so we offer you a handy
/// shortcut that allows to push the branches together with the notes.
///
/// branches - An array of branches to push. Must not be nil.
/// remote - The remote to push to. Must not be nil.
/// options - Options applied to the push operation. Can be NULL.
/// Recognized options are:
/// `GTRepositoryRemoteOptionsCredentialProvider`
/// referenceName - Reference name for notes, if they should be pushed together with the branches.
/// Use +[GTNote defaultReferenceNameWithError:] to push the default note reference.
/// Passing NULL here will make notes NOT to be pushed.
/// error - The error if one occurred. Can be NULL.
/// progressBlock - An optional callback for monitoring progress. May be NULL.
///
/// Returns YES if the push was successful, NO otherwise (and `error`, if provided,
/// will point to an error describing what happened).
- (BOOL)pushBranches:(NSArray<GTBranch *> *)branches toRemote:(GTRemote *)remote withOptions:(nullable NSDictionary *)options withNotesReferenceName:(nullable NSString *)notesReferenceName error:(NSError **)error progress:(nullable void (^)(unsigned int current, unsigned int total, size_t bytes, BOOL *stop))progressBlock;

/// Push a given Git notes reference name to a remote.
///
/// noteReferenceName - Name of the notes reference. If NULL, will default to whatever the default is (e.g. "refs/notes/commits")
/// remote - The remote to push to. Must not be nil.
/// options - Options applied to the push operation. Can be NULL.
/// Recognized options are:
/// `GTRepositoryRemoteOptionsCredentialProvider`
/// error - The error if one occurred. Can be NULL.
/// progressBlock - An optional callback for monitoring progress. May be NULL.
///
/// Returns YES if the push was successful, NO otherwise (and `error`, if provided,
/// will point to an error describing what happened).
- (BOOL)pushNotes:(nullable NSString *)noteReferenceName toRemote:(GTRemote *)remote withOptions:(nullable NSDictionary *)options error:(NSError **)error progress:(nullable void (^)(unsigned int current, unsigned int total, size_t bytes, BOOL *stop))progressBlock;

/// Delete a remote branch
///
/// branch - The branch to push. Must not be nil.
Expand Down
33 changes: 33 additions & 0 deletions ObjectiveGit/GTRepository+RemoteOperations.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
#import "NSArray+StringArray.h"
#import "NSError+Git.h"
#import "GTRepository+References.h"
#import "GTNote.h"

#import "git2/errors.h"
#import "git2/remote.h"
#import "git2/notes.h"
#import "git2/buffer.h"

NSString *const GTRepositoryRemoteOptionsCredentialProvider = @"GTRepositoryRemoteOptionsCredentialProvider";
NSString *const GTRepositoryRemoteOptionsFetchPrune = @"GTRepositoryRemoteOptionsFetchPrune";
Expand Down Expand Up @@ -172,6 +175,10 @@ - (BOOL)pushBranch:(GTBranch *)branch toRemote:(GTRemote *)remote withOptions:(N
}

- (BOOL)pushBranches:(NSArray *)branches toRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemotePushTransferProgressBlock)progressBlock {
return [self pushBranches:branches toRemote:remote withOptions:options withNotesReferenceName:nil error:error progress:progressBlock];
}

- (BOOL)pushBranches:(NSArray *)branches toRemote:(GTRemote *)remote withOptions:(NSDictionary *)options withNotesReferenceName:(NSString *)referenceName error:(NSError **)error progress:(GTRemotePushTransferProgressBlock)progressBlock {
NSParameterAssert(branches != nil);
NSParameterAssert(branches.count != 0);
NSParameterAssert(remote != nil);
Expand All @@ -194,10 +201,36 @@ - (BOOL)pushBranches:(NSArray *)branches toRemote:(GTRemote *)remote withOptions

[refspecs addObject:[NSString stringWithFormat:@"refs/heads/%@:%@", branch.shortName, remoteBranchReference]];
}

// Also push the notes reference, if needed.
if (referenceName != nil) {
// but check whether the reference exists for the repo, otherwise, our push will fail
GTReference *notesRef = [self lookUpReferenceWithName:referenceName error:nil];

if (notesRef != nil) {
[refspecs addObject:[NSString stringWithFormat:@"%@:%@", referenceName, referenceName]];
}
}

return [self pushRefspecs:refspecs toRemote:remote withOptions:options error:error progress:progressBlock];
}

- (BOOL)pushNotes:(NSString *)noteRef toRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error progress:(GTRemotePushTransferProgressBlock)progressBlock {
NSParameterAssert(remote != nil);

if (noteRef == nil) {
noteRef = [GTNote defaultReferenceNameForRepository:self error:error];

if (noteRef == nil) return NO;
}

GTReference *notesReference = [self lookUpReferenceWithName:noteRef error:error];

if (notesReference == nil) return NO;

return [self pushRefspecs:@[[NSString stringWithFormat:@"%@:%@", noteRef, noteRef]] toRemote:remote withOptions:options error:error progress:progressBlock];
}

#pragma mark - Deletion (Public)
- (BOOL)deleteBranch:(GTBranch *)branch fromRemote:(GTRemote *)remote withOptions:(NSDictionary *)options error:(NSError **)error {
NSParameterAssert(branch != nil);
Expand Down
43 changes: 43 additions & 0 deletions ObjectiveGit/GTRepository.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
@class GTTag;
@class GTTree;
@class GTRemote;
@class GTNote;

NS_ASSUME_NONNULL_BEGIN

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

/// Creates a new note in this repo (using a default notes reference, e.g. "refs/notes/commits")
///
/// note - Note text.
/// theTarget - Object (usually a commit) to which this note refers to.
/// This object must belong to this repository.
/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits")
/// author - Signature of the author for this note, and
/// of the note creation time
/// committer - Signature of the committer for this note.
/// overwrite - If set to YES, the note will be overwritten if it already exists.
/// error - Will be filled with a NSError object in case of error.
/// May be NULL.
///
/// Returns the newly created note or nil on error.
- (nullable GTNote *)createNote:(NSString *)note target:(GTObject *)theTarget referenceName:(nullable NSString *)referenceName author:(GTSignature *)author committer:(GTSignature *)committer overwriteIfExists:(BOOL)overwrite error:(NSError **)error;

/// Removes a note attached to object in this repo
///
/// parentObject - Object (usually a commit) to which the note to be removed is attached to.
/// This object must belong to this repository.
/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits")
/// author - Signature of the author for this note removal, and
/// of the note removal time
/// committer - Signature of the committer for this note removal.
/// error - Will be filled with a NSError object in case of error.
/// May be NULL.
///
/// Returns the YES on success and NO on error.
- (BOOL)removeNoteFromObject:(GTObject *)parentObject referenceName:(nullable NSString *)referenceName author:(GTSignature *)author committer:(GTSignature *)committer error:(NSError **)error;

/// Enumerates through all stored notes in this repo
///
/// referenceName - Name for the notes reference in the repo, or nil for default ("refs/notes/commits")
/// error - Will be filled with a NSError object in case of error.
/// May be NULL.
/// block - A block to be called on each encountered note object. The block accepts
/// a reference to `note`, an `object` that is annotated with the note.
/// If the block sets `stop` to YES, the iterator is finished.
///
/// Returns YES on overall success or NO on error of any kind.
- (BOOL)enumerateNotesWithReferenceName:(nullable NSString *)referenceName error:(NSError **)error usingBlock:(void (^)(GTNote *note, GTObject *object, BOOL *stop))block;

@end

NS_ASSUME_NONNULL_END
Loading