Skip to content

Commit be6ce3c

Browse files
committed
WIP: worktree support
1 parent a0593ed commit be6ce3c

File tree

6 files changed

+309
-0
lines changed

6 files changed

+309
-0
lines changed

ObjectiveGit/GTRepository+Worktree.h

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// GTRepository+GTRepository_Worktree.h
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Etienne on 25/07/2017.
6+
// Copyright © 2017 GitHub, Inc. All rights reserved.
7+
//
8+
9+
#import <ObjectiveGit/ObjectiveGit.h>
10+
11+
@class GTWorktree;
12+
13+
NS_ASSUME_NONNULL_BEGIN
14+
15+
@interface GTRepository (Worktree)
16+
17+
/// Is this the worktree of another repository ?
18+
@property (nonatomic, readonly, getter = isWorktree) BOOL worktree;
19+
20+
/// The URL for the underlying repository's git directory.
21+
/// Returns the same as -gitDirectoryURL if this is not a worktree.
22+
@property (nonatomic, readonly, strong) NSURL *commonGitDirectoryURL;
23+
24+
+ (instancetype _Nullable)repositoryWithWorktree:(GTWorktree *)worktree error:(NSError **)error;
25+
26+
- (instancetype _Nullable)initWithWorktree:(GTWorktree *)worktree error:(NSError **)error;
27+
28+
- (GTReference * _Nullable)HEADReferenceInWorktreeWithName:(NSString *)name error:(NSError **)error;
29+
30+
- (BOOL)isHEADDetached:(BOOL *)detached inWorktreeWithName:(NSString *)name error:(NSError **)error;
31+
32+
- (BOOL)setWorkingDirectoryURL:(NSURL *)URL updateGitLink:(BOOL)update error:(NSError **)error;
33+
34+
- (NSArray <NSString *> * _Nullable)worktreeNamesWithError:(NSError **)error;
35+
36+
- (GTWorktree * _Nullable)lookupWorktreeWithName:(NSString *)name error:(NSError **)error;
37+
38+
- (GTWorktree * _Nullable)openWorktree:(NSError **)error;
39+
40+
@end
41+
42+
NS_ASSUME_NONNULL_END

ObjectiveGit/GTRepository+Worktree.m

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//
2+
// GTRepository+Worktree.m
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Etienne on 25/07/2017.
6+
// Copyright © 2017 GitHub, Inc. All rights reserved.
7+
//
8+
9+
#import "GTRepository+Worktree.h"
10+
11+
@implementation GTRepository (Worktree)
12+
13+
+ (instancetype)repositoryWithWorktree:(GTWorktree *)worktree error:(NSError **)error {
14+
return [[self alloc] initWithWorktree:worktree error:error];
15+
}
16+
17+
- (instancetype)initWithWorktree:(GTWorktree *)worktree error:(NSError **)error {
18+
NSParameterAssert(worktree != nil);
19+
20+
git_repository *repo;
21+
int gitError = git_repository_open_from_worktree(&repo, worktree.git_worktree);
22+
if (gitError != GIT_OK) {
23+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to open worktree"];
24+
return nil;
25+
}
26+
return [self initWithGitRepository:repo];
27+
}
28+
29+
- (BOOL)isWorktree {
30+
return (BOOL)git_repository_is_worktree(self.git_repository);
31+
}
32+
33+
- (NSURL *)commonGitDirectoryURL {
34+
return [NSURL fileURLWithPath:@(git_repository_commondir(self.git_repository)) isDirectory:YES];
35+
}
36+
37+
- (GTReference *)HEADReferenceInWorktreeWithName:(NSString *)name error:(NSError **)error {
38+
NSParameterAssert(name != nil);
39+
40+
git_reference *ref;
41+
int gitError = git_repository_head_for_worktree(&ref, self.git_repository, name.UTF8String);
42+
if (gitError != GIT_OK) {
43+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to resolve HEAD in worktree"];
44+
return nil;
45+
}
46+
47+
return [[GTReference alloc] initWithGitReference:ref repository:self];
48+
}
49+
50+
- (BOOL)isHEADDetached:(BOOL *)detached inWorktreeWithName:(NSString *)name error:(NSError **)error {
51+
NSParameterAssert(detached != nil);
52+
NSParameterAssert(name != nil);
53+
54+
int gitError = git_repository_head_detached_for_worktree(self.git_repository, name.UTF8String);
55+
if (gitError < 0) {
56+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to resolve HEAD in worktree"];
57+
return NO;
58+
}
59+
60+
*detached = (gitError == 1);
61+
62+
return YES;
63+
}
64+
65+
- (BOOL)setWorkingDirectoryURL:(NSURL *)URL updateGitLink:(BOOL)update error:(NSError **)error {
66+
NSParameterAssert(URL != nil);
67+
68+
int gitError = git_repository_set_workdir(self.git_repository, URL.fileSystemRepresentation, update);
69+
if (gitError != GIT_OK) {
70+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to set workdir"];
71+
return NO;
72+
}
73+
74+
return YES;
75+
}
76+
77+
- (NSArray<NSString *> *)worktreeNamesWithError:(NSError **)error {
78+
git_strarray names;
79+
int gitError = git_worktree_list(&names, self.git_repository);
80+
if (gitError != GIT_OK) {
81+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to load worktree names"];
82+
return nil;
83+
}
84+
85+
return [NSArray git_arrayWithStrarray:names];
86+
}
87+
88+
- (GTWorktree *)lookupWorktreeWithName:(NSString *)name error:(NSError **)error {
89+
NSParameterAssert(name != nil);
90+
91+
git_worktree *worktree;
92+
int gitError = git_worktree_lookup(&worktree, self.git_repository, name.UTF8String);
93+
if (gitError != GIT_OK) {
94+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to lookup worktree"];
95+
return nil;
96+
}
97+
98+
return [[GTWorktree alloc] initWithGitWorktree:worktree];
99+
}
100+
101+
- (GTWorktree *)openWorktree:(NSError **)error {
102+
git_worktree *worktree;
103+
int gitError = git_worktree_open_from_repository(&worktree, self.git_repository);
104+
if (gitError != GIT_OK) {
105+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to open worktree"];
106+
return nil;
107+
}
108+
109+
return [[GTWorktree alloc] initWithGitWorktree:worktree];
110+
}
111+
112+
@end

ObjectiveGit/GTWorktree.h

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// GTWorktree.h
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Etienne on 25/07/2017.
6+
// Copyright © 2017 GitHub, Inc. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
#import "GTRepository.h"
12+
13+
#import "git2/worktree.h"
14+
15+
typedef struct {
16+
BOOL lock;
17+
} GTWorktreeAddOptions;
18+
19+
@interface GTWorktree : NSObject
20+
21+
+ (instancetype)addWorktreeWithName:(NSString *)name URL:(NSURL *)worktreeURL forRepository:(GTRepository *)repository options:(const GTWorktreeAddOptions *)options error:(NSError **)error;
22+
23+
- (instancetype)initWithGitWorktree:(git_worktree *)worktree;
24+
25+
/// The underlying `git_worktree` object.
26+
- (git_worktree *)git_worktree __attribute__((objc_returns_inner_pointer));
27+
28+
- (BOOL)isValid:(NSError **)error;
29+
30+
- (BOOL)lockWithReason:(NSString *)reason error:(NSError **)error;
31+
- (BOOL)unlock:(BOOL *)wasLocked error:(NSError **)error;
32+
33+
- (BOOL)isLocked:(BOOL *)locked reason:(NSString **)reason error:(NSError **)error;
34+
35+
@end

ObjectiveGit/GTWorktree.m

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//
2+
// GTWorktree.m
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Etienne on 25/07/2017.
6+
// Copyright © 2017 GitHub, Inc. All rights reserved.
7+
//
8+
9+
#import "NSError+Git.h"
10+
#import "GTWorktree.h"
11+
12+
#import "git2/errors.h"
13+
#import "git2/buffer.h"
14+
15+
@interface GTWorktree ()
16+
@property (nonatomic, assign, readonly) git_worktree *git_worktree;
17+
@end
18+
19+
@implementation GTWorktree
20+
21+
+ (instancetype)addWorktreeWithName:(NSString *)name URL:(NSURL *)worktreeURL forRepository:(GTRepository *)repository options:(const GTWorktreeAddOptions *)options error:(NSError **)error {
22+
git_worktree *worktree;
23+
git_worktree_add_options git_options = GIT_WORKTREE_ADD_OPTIONS_INIT;
24+
25+
if (options) {
26+
git_options.lock = options->lock;
27+
}
28+
29+
int gitError = git_worktree_add(&worktree, repository.git_repository, name.UTF8String, worktreeURL.fileSystemRepresentation, &git_options);
30+
if (gitError != GIT_OK) {
31+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to add worktree"];
32+
return nil;
33+
}
34+
35+
return [[self alloc] initWithGitWorktree:worktree];
36+
}
37+
38+
- (instancetype)initWithGitWorktree:(git_worktree *)worktree {
39+
self = [super init];
40+
if (!self) return nil;
41+
42+
_git_worktree = worktree;
43+
44+
return self;
45+
}
46+
47+
- (void)dealloc {
48+
git_worktree_free(_git_worktree);
49+
}
50+
51+
- (BOOL)isValid:(NSError **)error {
52+
int gitError = git_worktree_validate(self.git_worktree);
53+
if (gitError < 0) {
54+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to validate worktree"];
55+
return NO;
56+
}
57+
58+
return YES;
59+
}
60+
61+
- (BOOL)lockWithReason:(NSString *)reason error:(NSError **)error {
62+
int gitError = git_worktree_lock(self.git_worktree, (char *)reason.UTF8String); /* WIP: I don't like that cast */
63+
if (gitError != GIT_OK) {
64+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to lock worktree"];
65+
return NO;
66+
}
67+
68+
return YES;
69+
}
70+
71+
- (BOOL)unlock:(BOOL *)wasLocked error:(NSError **)error {
72+
int gitError = git_worktree_unlock(self.git_worktree);
73+
if (gitError < 0) {
74+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to unlock worktree"];
75+
return NO;
76+
}
77+
78+
if (wasLocked) {
79+
*wasLocked = (gitError == 0);
80+
}
81+
82+
return YES;
83+
}
84+
85+
- (BOOL)isLocked:(BOOL *)locked reason:(NSString **)reason error:(NSError **)error {
86+
git_buf reasonBuf;
87+
int gitError = git_worktree_is_locked(&reasonBuf, self.git_worktree);
88+
if (gitError < 0) {
89+
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to check lock state of worktree"];
90+
return NO;
91+
}
92+
93+
if (locked) *locked = (gitError > 0);
94+
95+
/* WIP: reason */
96+
97+
return YES;
98+
}
99+
100+
@end

ObjectiveGit/ObjectiveGit.h

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ FOUNDATION_EXPORT const unsigned char ObjectiveGitVersionString[];
4242
#import <ObjectiveGit/GTRepository+Reset.h>
4343
#import <ObjectiveGit/GTRepository+Pull.h>
4444
#import <ObjectiveGit/GTRepository+Merging.h>
45+
#import <ObjectiveGit/GTRepository+Worktree.h>
46+
#import <ObjectiveGit/GTWorktree.h>
4547
#import <ObjectiveGit/GTEnumerator.h>
4648
#import <ObjectiveGit/GTCommit.h>
4749
#import <ObjectiveGit/GTCredential.h>

ObjectiveGitFramework.xcodeproj/project.pbxproj

+18
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,15 @@
9494
4D79C0EE17DF9F4D00997DE4 /* GTCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D79C0EC17DF9F4D00997DE4 /* GTCredential.h */; settings = {ATTRIBUTES = (Public, ); }; };
9595
4D79C0EF17DF9F4D00997DE4 /* GTCredential.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */; };
9696
4DBA4A3217DA73CE006CD5F5 /* GTRemoteSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */; };
97+
4DC3720D1F27CD96003CD3CE /* GTRepository+Worktree.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC3720B1F27CD96003CD3CE /* GTRepository+Worktree.h */; settings = {ATTRIBUTES = (Public, ); }; };
98+
4DC3720E1F27CD96003CD3CE /* GTRepository+Worktree.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC3720C1F27CD96003CD3CE /* GTRepository+Worktree.m */; };
99+
4DC372111F27D6D3003CD3CE /* GTWorktree.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */; settings = {ATTRIBUTES = (Public, ); }; };
100+
4DC372121F27D6D3003CD3CE /* GTWorktree.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC372101F27D6D3003CD3CE /* GTWorktree.m */; };
97101
4DC55AE51AD859AD0032563C /* GTCheckoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
98102
4DC55AE61AD859AD0032563C /* GTCheckoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
99103
4DC55AE71AD859AD0032563C /* GTCheckoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */; };
100104
4DC55AE81AD859AD0032563C /* GTCheckoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */; };
105+
4DE935D21FCB0096003CD3CE /* GTWorktree.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */; settings = {ATTRIBUTES = (Public, ); }; };
101106
4DFFB15B183AA8D600D1565E /* GTRepository+RemoteOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DFFB159183AA8D600D1565E /* GTRepository+RemoteOperations.h */; settings = {ATTRIBUTES = (Public, ); }; };
102107
4DFFB15C183AA8D600D1565E /* GTRepository+RemoteOperations.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFFB15A183AA8D600D1565E /* GTRepository+RemoteOperations.m */; };
103108
55C8055013861FE7004DCB0F /* GTObjectDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C8054D13861F34004DCB0F /* GTObjectDatabase.m */; };
@@ -491,6 +496,10 @@
491496
4D79C0ED17DF9F4D00997DE4 /* GTCredential.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTCredential.m; sourceTree = "<group>"; };
492497
4D79C0F617DFAA7100997DE4 /* GTCredential+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTCredential+Private.h"; sourceTree = "<group>"; };
493498
4DBA4A3117DA73CE006CD5F5 /* GTRemoteSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTRemoteSpec.m; sourceTree = "<group>"; };
499+
4DC3720B1F27CD96003CD3CE /* GTRepository+Worktree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Worktree.h"; sourceTree = "<group>"; };
500+
4DC3720C1F27CD96003CD3CE /* GTRepository+Worktree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTRepository+Worktree.m"; sourceTree = "<group>"; };
501+
4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTWorktree.h; sourceTree = "<group>"; };
502+
4DC372101F27D6D3003CD3CE /* GTWorktree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTWorktree.m; sourceTree = "<group>"; };
494503
4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTCheckoutOptions.h; sourceTree = "<group>"; };
495504
4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTCheckoutOptions.m; sourceTree = "<group>"; };
496505
4DE864341794A37E00371A65 /* GTRepository+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTRepository+Private.h"; sourceTree = "<group>"; };
@@ -898,6 +907,8 @@
898907
88B2131B1B20E785005CF2C5 /* GTRepository+References.m */,
899908
23F39FAB1C86DB1C00849F3C /* GTRepository+Merging.h */,
900909
23F39FAC1C86DB1C00849F3C /* GTRepository+Merging.m */,
910+
4DC3720B1F27CD96003CD3CE /* GTRepository+Worktree.h */,
911+
4DC3720C1F27CD96003CD3CE /* GTRepository+Worktree.m */,
901912
BDD8AE6D13131B8800CB5D40 /* GTEnumerator.h */,
902913
BDD8AE6E13131B8800CB5D40 /* GTEnumerator.m */,
903914
BD6C22A71314625800992935 /* GTObject.h */,
@@ -967,6 +978,8 @@
967978
6EEB51A0199D62B9001D72C0 /* GTFetchHeadEntry.m */,
968979
4DC55AE31AD859AD0032563C /* GTCheckoutOptions.h */,
969980
4DC55AE41AD859AD0032563C /* GTCheckoutOptions.m */,
981+
4DC3720F1F27D6D3003CD3CE /* GTWorktree.h */,
982+
4DC372101F27D6D3003CD3CE /* GTWorktree.m */,
970983
);
971984
path = ObjectiveGit;
972985
sourceTree = "<group>";
@@ -1081,6 +1094,7 @@
10811094
BDD627991318391200DE34D1 /* GTBlob.h in Headers */,
10821095
886E622A18AEBF75000611A0 /* GTFilterSource.h in Headers */,
10831096
BDD62924131C03D600DE34D1 /* GTTag.h in Headers */,
1097+
4DC372111F27D6D3003CD3CE /* GTWorktree.h in Headers */,
10841098
88BC0E5018EF4F3600C7D0E6 /* GTRepository+Reset.h in Headers */,
10851099
BDFAF9C3131C1845000508BC /* GTIndex.h in Headers */,
10861100
BDFAF9C9131C1868000508BC /* GTIndexEntry.h in Headers */,
@@ -1108,6 +1122,7 @@
11081122
55C8057E13875C1B004DCB0F /* NSString+Git.h in Headers */,
11091123
30A3D6541667F11C00C49A39 /* GTDiff.h in Headers */,
11101124
3011D86B1668E48500CE3409 /* GTDiffFile.h in Headers */,
1125+
4DC3720D1F27CD96003CD3CE /* GTRepository+Worktree.h in Headers */,
11111126
3011D8711668E78500CE3409 /* GTDiffHunk.h in Headers */,
11121127
880EE66118AE700500B82455 /* GTFilter.h in Headers */,
11131128
30FDC07F16835A8100654BF0 /* GTDiffLine.h in Headers */,
@@ -1143,6 +1158,7 @@
11431158
D01B6F4D19F82F8700D411BC /* GTRemote.h in Headers */,
11441159
D01B6F1519F82F7B00D411BC /* NSData+Git.h in Headers */,
11451160
D01B6F6119F82FA600D411BC /* GTFilterSource.h in Headers */,
1161+
4DE935D21FCB0096003CD3CE /* GTWorktree.h in Headers */,
11461162
D0E0171519F9AD820019930C /* ObjectiveGit.h in Headers */,
11471163
88B2131D1B20E785005CF2C5 /* GTRepository+References.h in Headers */,
11481164
D01B6F4919F82F8700D411BC /* GTOdbObject.h in Headers */,
@@ -1473,6 +1489,7 @@
14731489
30DCBA6517B45A78009B0EBD /* GTRepository+Status.m in Sources */,
14741490
BD6C235413146E6A00992935 /* GTObject.m in Sources */,
14751491
4DC55AE71AD859AD0032563C /* GTCheckoutOptions.m in Sources */,
1492+
4DC3720E1F27CD96003CD3CE /* GTRepository+Worktree.m in Sources */,
14761493
BD6C254613148DD300992935 /* GTSignature.m in Sources */,
14771494
BD6B0412131496B8001909D0 /* GTTree.m in Sources */,
14781495
BD6B0418131496CC001909D0 /* GTTreeEntry.m in Sources */,
@@ -1494,6 +1511,7 @@
14941511
88EB7E4E14AEBA600046FEA4 /* GTConfiguration.m in Sources */,
14951512
883CD6AC1600EBC600F57354 /* GTRemote.m in Sources */,
14961513
30DCBA7317B4791A009B0EBD /* NSArray+StringArray.m in Sources */,
1514+
4DC372121F27D6D3003CD3CE /* GTWorktree.m in Sources */,
14971515
4DFFB15C183AA8D600D1565E /* GTRepository+RemoteOperations.m in Sources */,
14981516
88F05AC61601209A00B7AD1D /* ObjectiveGit.m in Sources */,
14991517
30A3D6561667F11C00C49A39 /* GTDiff.m in Sources */,

0 commit comments

Comments
 (0)