5 // Created by Pieter de Bie on 13-06-08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
9 #import "PBGitRepository.h"
10 #import "PBGitCommit.h"
11 #import "PBDetailController.h"
13 #import "NSFileHandleExt.h"
14 #import "PBEasyPipe.h"
16 NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain";
18 @implementation PBGitRepository
20 @synthesize revisionList, branches, currentBranch;
21 static NSString* gitPath;
25 // Try to find the path of the Git binary
26 char* path = getenv("GIT_PATH");
28 gitPath = [NSString stringWithCString:path];
32 // No explicit path. Try it with "which"
33 gitPath = [PBEasyPipe outputForCommand:@"/usr/bin/which" withArgs:[NSArray arrayWithObject:@"git"]];
34 if (gitPath.length > 0)
37 // Still no path. Let's try some default locations.
38 NSArray* locations = [NSArray arrayWithObjects:@"/opt/local/bin/git", @"/sw/bin/git", @"/opt/git/bin/git", nil];
39 for (NSString* location in locations) {
40 if ([[NSFileManager defaultManager] fileExistsAtPath:location]) {
46 NSLog(@"Could not find a git binary!");
49 - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
52 *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain
54 userInfo:[NSDictionary dictionaryWithObject:@"Reading files is not supported." forKey:NSLocalizedFailureReasonErrorKey]];
59 + (NSURL*)gitDirForURL:(NSURL*)repositoryURL;
61 NSString* repositoryPath = [repositoryURL path];
62 NSURL* gitDirURL = nil;
64 if ([repositoryPath hasSuffix:@".git"]) {
65 gitDirURL = [NSURL fileURLWithPath:repositoryPath];
67 // Use rev-parse to find the .git dir for the repository being opened
68 NSString* newPath = [PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"rev-parse", @"--git-dir", nil] inDir:repositoryPath];
69 if ([newPath isEqualToString:@".git"]) {
70 gitDirURL = [NSURL fileURLWithPath:[repositoryPath stringByAppendingPathComponent:@".git"]];
71 } else if ([newPath length] > 0) {
72 gitDirURL = [NSURL fileURLWithPath:newPath];
79 // For a given path inside a repository, return either the .git dir
80 // (for a bare repo) or the directory above the .git dir otherwise
81 + (NSURL*)baseDirForURL:(NSURL*)repositoryURL;
83 NSURL* gitDirURL = [self gitDirForURL:repositoryURL];
84 NSString* repositoryPath = [gitDirURL path];
86 if (![[PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"rev-parse", @"--is-bare-repository", nil] inDir:repositoryPath] isEqualToString:@"true"]) {
87 repositoryURL = [NSURL fileURLWithPath:[[repositoryURL path] stringByDeletingLastPathComponent]];
93 - (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError
97 if (![fileWrapper isDirectory]) {
99 NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Reading files is not supported.", [fileWrapper filename]]
100 forKey:NSLocalizedRecoverySuggestionErrorKey];
101 *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo];
104 NSURL* gitDirURL = [PBGitRepository gitDirForURL:[self fileURL]];
106 [self setFileURL:gitDirURL];
108 } else if (outError) {
109 NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ does not appear to be a git repository.", [fileWrapper filename]]
110 forKey:NSLocalizedRecoverySuggestionErrorKey];
111 *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo];
116 [self readCurrentBranch];
117 revisionList = [[PBGitRevList alloc] initWithRepository:self andRevListParameters:[NSArray array]];
124 // The fileURL the document keeps is to the .git dir, but that’s pretty
125 // useless for display in the window title bar, so we show the directory above
126 - (NSString*)displayName
128 NSString* displayName = self.fileURL.path.lastPathComponent;
129 if ([displayName isEqualToString:@".git"])
130 displayName = [self.fileURL.path stringByDeletingLastPathComponent].lastPathComponent;
134 // Overridden to create our custom window controller
135 - (void)makeWindowControllers
137 PBDetailController* controller = [[PBDetailController alloc] initWithRepository:self];
138 [self addWindowController:controller];
139 [controller release];
142 - (void) readBranches
144 NSString* output = [PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"for-each-ref", @"refs/heads", nil] inDir: self.fileURL.path];
145 NSArray* lines = [output componentsSeparatedByString:@"\n"];
146 NSMutableArray* newBranches = [NSMutableArray array];
147 for (NSString* line in lines) {
148 NSString* substr = [line substringWithRange: NSMakeRange(40,19)];
149 if (![substr isEqualToString:@" commit\trefs/heads/"]) {
150 NSLog(@"Cannot parse branch %@. (%@)", line, substr);
153 NSString* branch = [line substringFromIndex:59];
154 [newBranches addObject: branch];
156 self.branches = newBranches;
159 - (void) readCurrentBranch
161 NSString* branch = [self parseSymbolicReference: @"HEAD"];
162 if (branch && [branch hasPrefix:@"refs/heads/"])
163 self.currentBranch = [branch substringFromIndex:11];
167 - (NSFileHandle*) handleForArguments:(NSArray *)args
169 NSString* gitDirArg = [@"--git-dir=" stringByAppendingString:self.fileURL.path];
170 NSMutableArray* arguments = [NSMutableArray arrayWithObject: gitDirArg];
171 [arguments addObjectsFromArray: args];
172 return [PBEasyPipe handleForCommand:gitPath withArgs:arguments];
175 - (NSFileHandle*) handleForCommand:(NSString *)cmd
177 NSArray* arguments = [cmd componentsSeparatedByString:@" "];
178 return [self handleForArguments:arguments];
181 - (NSString*) outputForCommand:(NSString *)cmd
183 NSArray* arguments = [cmd componentsSeparatedByString:@" "];
184 return [self outputForArguments: arguments];
187 - (NSString*) outputForArguments:(NSArray*) arguments
189 return [PBEasyPipe outputForCommand:gitPath withArgs:arguments inDir: self.fileURL.path];
192 - (NSString*) parseReference:(NSString *)reference
194 return [self outputForArguments:[NSArray arrayWithObjects: @"rev-parse", reference, nil]];
197 - (NSString*) parseSymbolicReference:(NSString*) reference
199 NSString* ref = [self outputForArguments:[NSArray arrayWithObjects: @"symbolic-ref", reference, nil]];
200 if ([ref hasPrefix:@"refs/"])