forked from nullmedium/gitx
/
PBGitRepository.m
218 lines (184 loc) · 7.15 KB
/
PBGitRepository.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//
// PBGitRepository.m
// GitTest
//
// Created by Pieter de Bie on 13-06-08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "PBGitRepository.h"
#import "PBGitCommit.h"
#import "PBDetailController.h"
#import "NSFileHandleExt.h"
#import "PBEasyPipe.h"
#import "PBGitRef.h"
NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain";
@implementation PBGitRepository
@synthesize revisionList, branches, currentBranch, refs;
static NSString* gitPath;
+ (void) initialize
{
// Try to find the path of the Git binary
char* path = getenv("GIT_PATH");
if (path != nil) {
gitPath = [NSString stringWithCString:path];
return;
}
// No explicit path. Try it with "which"
gitPath = [PBEasyPipe outputForCommand:@"/usr/bin/which" withArgs:[NSArray arrayWithObject:@"git"]];
if (gitPath.length > 0)
return;
// Still no path. Let's try some default locations.
NSArray* locations = [NSArray arrayWithObjects:@"/opt/local/bin/git", @"/sw/bin/git", @"/opt/git/bin/git", nil];
for (NSString* location in locations) {
if ([[NSFileManager defaultManager] fileExistsAtPath:location]) {
gitPath = location;
return;
}
}
NSLog(@"Could not find a git binary!");
}
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
if (outError) {
*outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain
code:0
userInfo:[NSDictionary dictionaryWithObject:@"Reading files is not supported." forKey:NSLocalizedFailureReasonErrorKey]];
}
return NO;
}
+ (NSURL*)gitDirForURL:(NSURL*)repositoryURL;
{
NSString* repositoryPath = [repositoryURL path];
NSURL* gitDirURL = nil;
if ([repositoryPath hasSuffix:@".git"]) {
gitDirURL = [NSURL fileURLWithPath:repositoryPath];
} else {
// Use rev-parse to find the .git dir for the repository being opened
NSString* newPath = [PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"rev-parse", @"--git-dir", nil] inDir:repositoryPath];
if ([newPath isEqualToString:@".git"]) {
gitDirURL = [NSURL fileURLWithPath:[repositoryPath stringByAppendingPathComponent:@".git"]];
} else if ([newPath length] > 0) {
gitDirURL = [NSURL fileURLWithPath:newPath];
}
}
return gitDirURL;
}
// For a given path inside a repository, return either the .git dir
// (for a bare repo) or the directory above the .git dir otherwise
+ (NSURL*)baseDirForURL:(NSURL*)repositoryURL;
{
NSURL* gitDirURL = [self gitDirForURL:repositoryURL];
NSString* repositoryPath = [gitDirURL path];
if (![[PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"rev-parse", @"--is-bare-repository", nil] inDir:repositoryPath] isEqualToString:@"true"]) {
repositoryURL = [NSURL fileURLWithPath:[[repositoryURL path] stringByDeletingLastPathComponent]];
}
return repositoryURL;
}
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError
{
BOOL success = NO;
if (![fileWrapper isDirectory]) {
if (outError) {
NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Reading files is not supported.", [fileWrapper filename]]
forKey:NSLocalizedRecoverySuggestionErrorKey];
*outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo];
}
} else {
NSURL* gitDirURL = [PBGitRepository gitDirForURL:[self fileURL]];
if (gitDirURL) {
[self setFileURL:gitDirURL];
success = YES;
} else if (outError) {
NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ does not appear to be a git repository.", [fileWrapper filename]]
forKey:NSLocalizedRecoverySuggestionErrorKey];
*outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo];
}
if (success) {
[self readRefs];
[self readCurrentBranch];
revisionList = [[PBGitRevList alloc] initWithRepository:self andRevListParameters:[NSArray array]];
}
}
return success;
}
// The fileURL the document keeps is to the .git dir, but that’s pretty
// useless for display in the window title bar, so we show the directory above
- (NSString*)displayName
{
NSString* displayName = self.fileURL.path.lastPathComponent;
if ([displayName isEqualToString:@".git"])
displayName = [self.fileURL.path stringByDeletingLastPathComponent].lastPathComponent;
return displayName;
}
// Overridden to create our custom window controller
- (void)makeWindowControllers
{
PBDetailController* controller = [[PBDetailController alloc] initWithRepository:self];
[self addWindowController:controller];
[controller release];
}
- (void) readRefs
{
NSString* output = [PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"for-each-ref", @"--format=%(refname) %(objecttype) %(objectname) %(*objectname)", @"refs", nil] inDir: self.fileURL.path];
NSArray* lines = [output componentsSeparatedByString:@"\n"];
NSMutableDictionary* newRefs = [NSMutableDictionary dictionary];
NSMutableArray* newBranches = [NSMutableArray array];
for (NSString* line in lines) {
NSArray* components = [line componentsSeparatedByString:@" "];
PBGitRef* ref = [PBGitRef refFromString:[components objectAtIndex:0]];
NSString* type = [components objectAtIndex:1];
NSString* sha;
if ([type isEqualToString:@"tag"] && [components count] == 4)
sha = [components objectAtIndex:3];
else
sha = [components objectAtIndex:2];
if ([[ref type] isEqualToString:@"head"])
[newBranches addObject: ref];
NSMutableArray* curRefs;
if (curRefs = [newRefs objectForKey:sha])
[curRefs addObject:ref];
else
[newRefs setObject:[NSMutableArray arrayWithObject:ref] forKey:sha];
}
self.branches = newBranches;
self.refs = newRefs;
}
- (void) readCurrentBranch
{
NSString* branch = [self parseSymbolicReference: @"HEAD"];
if (branch && [branch hasPrefix:@"refs/heads/"])
self.currentBranch = [branch substringFromIndex:11];
}
- (NSFileHandle*) handleForArguments:(NSArray *)args
{
NSString* gitDirArg = [@"--git-dir=" stringByAppendingString:self.fileURL.path];
NSMutableArray* arguments = [NSMutableArray arrayWithObject: gitDirArg];
[arguments addObjectsFromArray: args];
return [PBEasyPipe handleForCommand:gitPath withArgs:arguments];
}
- (NSFileHandle*) handleForCommand:(NSString *)cmd
{
NSArray* arguments = [cmd componentsSeparatedByString:@" "];
return [self handleForArguments:arguments];
}
- (NSString*) outputForCommand:(NSString *)cmd
{
NSArray* arguments = [cmd componentsSeparatedByString:@" "];
return [self outputForArguments: arguments];
}
- (NSString*) outputForArguments:(NSArray*) arguments
{
return [PBEasyPipe outputForCommand:gitPath withArgs:arguments inDir: self.fileURL.path];
}
- (NSString*) parseReference:(NSString *)reference
{
return [self outputForArguments:[NSArray arrayWithObjects: @"rev-parse", reference, nil]];
}
- (NSString*) parseSymbolicReference:(NSString*) reference
{
NSString* ref = [self outputForArguments:[NSArray arrayWithObjects: @"symbolic-ref", reference, nil]];
if ([ref hasPrefix:@"refs/"])
return ref;
return nil;
}
@end