Skip to content

Commit

Permalink
WIP: worktree support
Browse files Browse the repository at this point in the history
  • Loading branch information
tiennou committed Feb 15, 2018
1 parent be9fc32 commit a4ceb97
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 0 deletions.
42 changes: 42 additions & 0 deletions ObjectiveGit/GTRepository+Worktree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// GTRepository+GTRepository_Worktree.h
// ObjectiveGitFramework
//
// Created by Etienne on 25/07/2017.
// Copyright © 2017 GitHub, Inc. All rights reserved.
//

#import <ObjectiveGit/ObjectiveGit.h>

@class GTWorktree;

NS_ASSUME_NONNULL_BEGIN

@interface GTRepository (Worktree)

/// Is this the worktree of another repository ?
@property (nonatomic, readonly, getter = isWorktree) BOOL worktree;

/// The URL for the underlying repository's git directory.
/// Returns the same as -gitDirectoryURL if this is not a worktree.
@property (nonatomic, readonly, strong) NSURL *commonGitDirectoryURL;

+ (instancetype _Nullable)repositoryWithWorktree:(GTWorktree *)worktree error:(NSError **)error;

- (instancetype _Nullable)initWithWorktree:(GTWorktree *)worktree error:(NSError **)error;

- (GTReference * _Nullable)HEADReferenceInWorktreeWithName:(NSString *)name error:(NSError **)error;

- (BOOL)isHEADDetached:(BOOL *)detached inWorktreeWithName:(NSString *)name error:(NSError **)error;

- (BOOL)setWorkingDirectoryURL:(NSURL *)URL updateGitLink:(BOOL)update error:(NSError **)error;

- (NSArray <NSString *> * _Nullable)worktreeNamesWithError:(NSError **)error;

- (GTWorktree * _Nullable)lookupWorktreeWithName:(NSString *)name error:(NSError **)error;

- (GTWorktree * _Nullable)openWorktree:(NSError **)error;

@end

NS_ASSUME_NONNULL_END
112 changes: 112 additions & 0 deletions ObjectiveGit/GTRepository+Worktree.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// GTRepository+Worktree.m
// ObjectiveGitFramework
//
// Created by Etienne on 25/07/2017.
// Copyright © 2017 GitHub, Inc. All rights reserved.
//

#import "GTRepository+Worktree.h"

@implementation GTRepository (Worktree)

+ (instancetype)repositoryWithWorktree:(GTWorktree *)worktree error:(NSError **)error {
return [[self alloc] initWithWorktree:worktree error:error];
}

- (instancetype)initWithWorktree:(GTWorktree *)worktree error:(NSError **)error {
NSParameterAssert(worktree != nil);

git_repository *repo;
int gitError = git_repository_open_from_worktree(&repo, worktree.git_worktree);
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to open worktree"];
return nil;
}
return [self initWithGitRepository:repo];
}

- (BOOL)isWorktree {
return (BOOL)git_repository_is_worktree(self.git_repository);
}

- (NSURL *)commonGitDirectoryURL {
return [NSURL fileURLWithPath:@(git_repository_commondir(self.git_repository)) isDirectory:YES];
}

- (GTReference *)HEADReferenceInWorktreeWithName:(NSString *)name error:(NSError **)error {
NSParameterAssert(name != nil);

git_reference *ref;
int gitError = git_repository_head_for_worktree(&ref, self.git_repository, name.UTF8String);
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to resolve HEAD in worktree"];
return nil;
}

return [[GTReference alloc] initWithGitReference:ref repository:self];
}

- (BOOL)isHEADDetached:(BOOL *)detached inWorktreeWithName:(NSString *)name error:(NSError **)error {
NSParameterAssert(detached != nil);
NSParameterAssert(name != nil);

int gitError = git_repository_head_detached_for_worktree(self.git_repository, name.UTF8String);
if (gitError < 0) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to resolve HEAD in worktree"];
return NO;
}

*detached = (gitError == 1);

return YES;
}

- (BOOL)setWorkingDirectoryURL:(NSURL *)URL updateGitLink:(BOOL)update error:(NSError **)error {
NSParameterAssert(URL != nil);

int gitError = git_repository_set_workdir(self.git_repository, URL.fileSystemRepresentation, update);
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to set workdir"];
return NO;
}

return YES;
}

- (NSArray<NSString *> *)worktreeNamesWithError:(NSError **)error {
git_strarray names;
int gitError = git_worktree_list(&names, self.git_repository);
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to load worktree names"];
return nil;
}

return [NSArray git_arrayWithStrarray:names];
}

- (GTWorktree *)lookupWorktreeWithName:(NSString *)name error:(NSError **)error {
NSParameterAssert(name != nil);

git_worktree *worktree;
int gitError = git_worktree_lookup(&worktree, self.git_repository, name.UTF8String);
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to lookup worktree"];
return nil;
}

return [[GTWorktree alloc] initWithGitWorktree:worktree];
}

- (GTWorktree *)openWorktree:(NSError **)error {
git_worktree *worktree;
int gitError = git_worktree_open_from_repository(&worktree, self.git_repository);
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to open worktree"];
return nil;
}

return [[GTWorktree alloc] initWithGitWorktree:worktree];
}

@end
35 changes: 35 additions & 0 deletions ObjectiveGit/GTWorktree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// GTWorktree.h
// ObjectiveGitFramework
//
// Created by Etienne on 25/07/2017.
// Copyright © 2017 GitHub, Inc. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "GTRepository.h"

#import "git2/worktree.h"

typedef struct {
BOOL lock;
} GTWorktreeAddOptions;

@interface GTWorktree : NSObject

+ (instancetype)addWorktreeWithName:(NSString *)name URL:(NSURL *)worktreeURL forRepository:(GTRepository *)repository options:(const GTWorktreeAddOptions *)options error:(NSError **)error;

- (instancetype)initWithGitWorktree:(git_worktree *)worktree;

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

- (BOOL)isValid:(NSError **)error;

- (BOOL)lockWithReason:(NSString *)reason error:(NSError **)error;
- (BOOL)unlock:(BOOL *)wasLocked error:(NSError **)error;

- (BOOL)isLocked:(BOOL *)locked reason:(NSString **)reason error:(NSError **)error;

@end
100 changes: 100 additions & 0 deletions ObjectiveGit/GTWorktree.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// GTWorktree.m
// ObjectiveGitFramework
//
// Created by Etienne on 25/07/2017.
// Copyright © 2017 GitHub, Inc. All rights reserved.
//

#import "NSError+Git.h"
#import "GTWorktree.h"

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

@interface GTWorktree ()
@property (nonatomic, assign, readonly) git_worktree *git_worktree;
@end

@implementation GTWorktree

+ (instancetype)addWorktreeWithName:(NSString *)name URL:(NSURL *)worktreeURL forRepository:(GTRepository *)repository options:(const GTWorktreeAddOptions *)options error:(NSError **)error {
git_worktree *worktree;
git_worktree_add_options git_options = GIT_WORKTREE_ADD_OPTIONS_INIT;

if (options) {
git_options.lock = options->lock;
}

int gitError = git_worktree_add(&worktree, repository.git_repository, name.UTF8String, worktreeURL.fileSystemRepresentation, &git_options);
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to add worktree"];
return nil;
}

return [[self alloc] initWithGitWorktree:worktree];
}

- (instancetype)initWithGitWorktree:(git_worktree *)worktree {
self = [super init];
if (!self) return nil;

_git_worktree = worktree;

return self;
}

- (void)dealloc {
git_worktree_free(_git_worktree);
}

- (BOOL)isValid:(NSError **)error {
int gitError = git_worktree_validate(self.git_worktree);
if (gitError < 0) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to validate worktree"];
return NO;
}

return YES;
}

- (BOOL)lockWithReason:(NSString *)reason error:(NSError **)error {
int gitError = git_worktree_lock(self.git_worktree, (char *)reason.UTF8String); /* WIP: I don't like that cast */
if (gitError != GIT_OK) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to lock worktree"];
return NO;
}

return YES;
}

- (BOOL)unlock:(BOOL *)wasLocked error:(NSError **)error {
int gitError = git_worktree_unlock(self.git_worktree);
if (gitError < 0) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to unlock worktree"];
return NO;
}

if (wasLocked) {
*wasLocked = (gitError == 0);
}

return YES;
}

- (BOOL)isLocked:(BOOL *)locked reason:(NSString **)reason error:(NSError **)error {
git_buf reasonBuf;
int gitError = git_worktree_is_locked(&reasonBuf, self.git_worktree);
if (gitError < 0) {
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to check lock state of worktree"];
return NO;
}

if (locked) *locked = (gitError > 0);

/* WIP: reason */

return YES;
}

@end
2 changes: 2 additions & 0 deletions ObjectiveGit/ObjectiveGit.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ FOUNDATION_EXPORT const unsigned char ObjectiveGitVersionString[];
#import <ObjectiveGit/GTRepository+Reset.h>
#import <ObjectiveGit/GTRepository+Pull.h>
#import <ObjectiveGit/GTRepository+Merging.h>
#import <ObjectiveGit/GTRepository+Worktree.h>
#import <ObjectiveGit/GTWorktree.h>
#import <ObjectiveGit/GTEnumerator.h>
#import <ObjectiveGit/GTCommit.h>
#import <ObjectiveGit/GTCredential.h>
Expand Down
18 changes: 18 additions & 0 deletions ObjectiveGitFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,15 @@
4D79C0EE17DF9F4D00997DE4 /* GTCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D79C0EC17DF9F4D00997DE4 /* GTCredential.h */; settings = {ATTRIBUTES = (Public, ); }; };
4D79C0EF17DF9F4D00997DE4 /* GTCredential.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */; };
4DBA4A3217DA73CE006CD5F5 /* GTRemoteSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */; };
4DC3720D1F27CD96003CD3CE /* GTRepository+Worktree.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC3720B1F27CD96003CD3CE /* GTRepository+Worktree.h */; settings = {ATTRIBUTES = (Public, ); }; };
4DC3720E1F27CD96003CD3CE /* GTRepository+Worktree.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC3720C1F27CD96003CD3CE /* GTRepository+Worktree.m */; };
4DC372111F27D6D3003CD3CE /* GTWorktree.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */; settings = {ATTRIBUTES = (Public, ); }; };
4DC372121F27D6D3003CD3CE /* GTWorktree.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC372101F27D6D3003CD3CE /* GTWorktree.m */; };
4DC55AE51AD859AD0032563C /* GTCheckoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
4DC55AE61AD859AD0032563C /* GTCheckoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
4DC55AE71AD859AD0032563C /* GTCheckoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */; };
4DC55AE81AD859AD0032563C /* GTCheckoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */; };
4DE935D21FCB0096003CD3CE /* GTWorktree.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */; settings = {ATTRIBUTES = (Public, ); }; };
4DFFB15B183AA8D600D1565E /* GTRepository+RemoteOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DFFB159183AA8D600D1565E /* GTRepository+RemoteOperations.h */; settings = {ATTRIBUTES = (Public, ); }; };
4DFFB15C183AA8D600D1565E /* GTRepository+RemoteOperations.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFFB15A183AA8D600D1565E /* GTRepository+RemoteOperations.m */; };
55C8055013861FE7004DCB0F /* GTObjectDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */; };
Expand Down Expand Up @@ -491,6 +496,10 @@
4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTCredential.m; sourceTree = "<group>"; };
4D79C0F617DFAA7100997DE4 /* GTCredential+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTCredential+Private.h"; sourceTree = "<group>"; };
4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRemoteSpec.m; sourceTree = "<group>"; };
4DC3720B1F27CD96003CD3CE /* GTRepository+Worktree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Worktree.h"; sourceTree = "<group>"; };
4DC3720C1F27CD96003CD3CE /* GTRepository+Worktree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Worktree.m"; sourceTree = "<group>"; };
4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTWorktree.h; sourceTree = "<group>"; };
4DC372101F27D6D3003CD3CE /* GTWorktree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTWorktree.m; sourceTree = "<group>"; };
4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTCheckoutOptions.h; sourceTree = "<group>"; };
4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTCheckoutOptions.m; sourceTree = "<group>"; };
4DE864341794A37E00371A65 /* GTRepository+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Private.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -898,6 +907,8 @@
88B2131B1B20E785005CF2C5 /* GTRepository+References.m */,
23F39FAB1C86DB1C00849F3C /* GTRepository+Merging.h */,
23F39FAC1C86DB1C00849F3C /* GTRepository+Merging.m */,
4DC3720B1F27CD96003CD3CE /* GTRepository+Worktree.h */,
4DC3720C1F27CD96003CD3CE /* GTRepository+Worktree.m */,
BDD8AE6D13131B8800CB5D40 /* GTEnumerator.h */,
BDD8AE6E13131B8800CB5D40 /* GTEnumerator.m */,
BD6C22A71314625800992935 /* GTObject.h */,
Expand Down Expand Up @@ -967,6 +978,8 @@
6EEB51A0199D62B9001D72C0 /* GTFetchHeadEntry.m */,
4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */,
4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */,
4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */,
4DC372101F27D6D3003CD3CE /* GTWorktree.m */,
);
path = ObjectiveGit;
sourceTree = "<group>";
Expand Down Expand Up @@ -1081,6 +1094,7 @@
BDD627991318391200DE34D1 /* GTBlob.h in Headers */,
886E622A18AEBF75000611A0 /* GTFilterSource.h in Headers */,
BDD62924131C03D600DE34D1 /* GTTag.h in Headers */,
4DC372111F27D6D3003CD3CE /* GTWorktree.h in Headers */,
88BC0E5018EF4F3600C7D0E6 /* GTRepository+Reset.h in Headers */,
BDFAF9C3131C1845000508BC /* GTIndex.h in Headers */,
BDFAF9C9131C1868000508BC /* GTIndexEntry.h in Headers */,
Expand Down Expand Up @@ -1108,6 +1122,7 @@
55C8057E13875C1B004DCB0F /* NSString+Git.h in Headers */,
30A3D6541667F11C00C49A39 /* GTDiff.h in Headers */,
3011D86B1668E48500CE3409 /* GTDiffFile.h in Headers */,
4DC3720D1F27CD96003CD3CE /* GTRepository+Worktree.h in Headers */,
3011D8711668E78500CE3409 /* GTDiffHunk.h in Headers */,
880EE66118AE700500B82455 /* GTFilter.h in Headers */,
30FDC07F16835A8100654BF0 /* GTDiffLine.h in Headers */,
Expand Down Expand Up @@ -1143,6 +1158,7 @@
D01B6F4D19F82F8700D411BC /* GTRemote.h in Headers */,
D01B6F1519F82F7B00D411BC /* NSData+Git.h in Headers */,
D01B6F6119F82FA600D411BC /* GTFilterSource.h in Headers */,
4DE935D21FCB0096003CD3CE /* GTWorktree.h in Headers */,
D0E0171519F9AD820019930C /* ObjectiveGit.h in Headers */,
88B2131D1B20E785005CF2C5 /* GTRepository+References.h in Headers */,
D01B6F4919F82F8700D411BC /* GTOdbObject.h in Headers */,
Expand Down Expand Up @@ -1473,6 +1489,7 @@
30DCBA6517B45A78009B0EBD /* GTRepository+Status.m in Sources */,
BD6C235413146E6A00992935 /* GTObject.m in Sources */,
4DC55AE71AD859AD0032563C /* GTCheckoutOptions.m in Sources */,
4DC3720E1F27CD96003CD3CE /* GTRepository+Worktree.m in Sources */,
BD6C254613148DD300992935 /* GTSignature.m in Sources */,
BD6B0412131496B8001909D0 /* GTTree.m in Sources */,
BD6B0418131496CC001909D0 /* GTTreeEntry.m in Sources */,
Expand All @@ -1494,6 +1511,7 @@
88EB7E4E14AEBA600046FEA4 /* GTConfiguration.m in Sources */,
883CD6AC1600EBC600F57354 /* GTRemote.m in Sources */,
30DCBA7317B4791A009B0EBD /* NSArray+StringArray.m in Sources */,
4DC372121F27D6D3003CD3CE /* GTWorktree.m in Sources */,
4DFFB15C183AA8D600D1565E /* GTRepository+RemoteOperations.m in Sources */,
88F05AC61601209A00B7AD1D /* ObjectiveGit.m in Sources */,
30A3D6561667F11C00C49A39 /* GTDiff.m in Sources */,
Expand Down

0 comments on commit a4ceb97

Please sign in to comment.