// // 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" NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain"; @implementation PBGitRepository @synthesize revisionList, branches, currentBranch; 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 readBranches]; [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) readBranches { NSString* output = [PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"for-each-ref", @"refs/heads", nil] inDir: self.fileURL.path]; NSArray* lines = [output componentsSeparatedByString:@"\n"]; NSMutableArray* newBranches = [NSMutableArray array]; for (NSString* line in lines) { NSString* substr = [line substringWithRange: NSMakeRange(40,19)]; if (![substr isEqualToString:@" commit\trefs/heads/"]) { NSLog(@"Cannot parse branch %@. (%@)", line, substr); continue; } NSString* branch = [line substringFromIndex:59]; [newBranches addObject: branch]; } self.branches = newBranches; } - (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