Files
gitx/PBGitRepository.m
T

311 lines
9.3 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// 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 "PBGitWindowController.h"
#import "NSFileHandleExt.h"
#import "PBEasyPipe.h"
#import "PBGitRef.h"
#import "PBGitRevSpecifier.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",
@"/usr/local/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;
}
+ (BOOL) isBareRepository: (NSString*) path
{
return [[PBEasyPipe outputForCommand:gitPath withArgs:[NSArray arrayWithObjects:@"rev-parse", @"--is-bare-repository", nil] inDir:path] isEqualToString:@"true"];
}
+ (NSURL*)gitDirForURL:(NSURL*)repositoryURL;
{
NSString* repositoryPath = [repositoryURL path];
if ([self isBareRepository:repositoryPath])
return repositoryURL;
// 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"])
return [NSURL fileURLWithPath:[repositoryPath stringByAppendingPathComponent:@".git"]];
if ([newPath length] > 0)
return [NSURL fileURLWithPath:newPath];
return nil;
}
// 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 (![self isBareRepository:repositoryPath]) {
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 setup];
[self readCurrentBranch];
}
}
return success;
}
- (void) setup
{
self.branches = [NSMutableArray array];
[self reloadRefs];
revisionList = [[PBGitRevList alloc] initWithRepository:self];
}
- (id) initWithURL: (NSURL*) path andRevSpecifier:(PBGitRevSpecifier*) rev
{
NSURL* gitDirURL = [PBGitRepository gitDirForURL:path];
if (!gitDirURL)
return nil;
self = [self init];
[self setFileURL: gitDirURL];
[self setup];
[self selectBranch: [self addBranch: rev]];
return self;
}
// The fileURL the document keeps is to the .git dir, but thats 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
{
PBGitWindowController* controller = [[PBGitWindowController alloc] initWithRepository:self];
[self addWindowController:controller];
[controller release];
}
- (void) addRef: (PBGitRef *) ref fromParameters: (NSArray *) components
{
NSString* type = [components objectAtIndex:1];
NSString* sha;
if ([type isEqualToString:@"tag"] && [components count] == 4)
sha = [components objectAtIndex:3];
else
sha = [components objectAtIndex:2];
NSMutableArray* curRefs;
if (curRefs = [refs objectForKey:sha])
[curRefs addObject:ref];
else
[refs setObject:[NSMutableArray arrayWithObject:ref] forKey:sha];
}
// reloadRefs: reload all refs in the repository, like in readRefs
// To stay compatible, this does not remove a ref from the branches list
// even after it has been deleted.
// returns YES when a ref was changed
- (BOOL) reloadRefs
{
BOOL ret = NO;
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"];
refs = [NSMutableDictionary dictionary];
for (NSString* line in lines) {
NSArray* components = [line componentsSeparatedByString:@" "];
// First do the ref matching. If this ref is new, add it to our ref list
PBGitRef *newRef = [PBGitRef refFromString:[components objectAtIndex:0]];
PBGitRevSpecifier* revSpec = [[PBGitRevSpecifier alloc] initWithRef:newRef];
if ([[newRef type] isEqualToString:@"head"] || [[newRef type] isEqualToString:@"remote"])
if ([self addBranch:revSpec] != revSpec)
ret = YES;
// Also add this ref to the refs list
[self addRef:newRef fromParameters:components];
}
self.refs = refs;
return ret;
}
- (PBGitRevSpecifier*) headRef
{
NSString* branch = [self parseSymbolicReference: @"HEAD"];
if (branch && [branch hasPrefix:@"refs/heads/"])
return [[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:branch]];
return [[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:@"HEAD"]];
}
// Returns either this object, or an existing, equal object
- (PBGitRevSpecifier*) addBranch: (PBGitRevSpecifier*) rev
{
if ([[rev parameters] count] == 0)
rev = [self headRef];
// First check if the branch doesn't exist already
for (PBGitRevSpecifier* r in branches)
if ([rev isEqualTo: r])
return r;
[self willChangeValueForKey:@"branches"];
[branches addObject: rev];
[self didChangeValueForKey:@"branches"];
return rev;
}
- (void) selectBranch: (PBGitRevSpecifier*) rev
{
int i;
for (i = 0; i < [branches count]; i++) {
PBGitRevSpecifier* aRev = [branches objectAtIndex:i];
if (rev == aRev) {
self.currentBranch = [NSIndexSet indexSetWithIndex:i];
return;
}
}
}
- (void) readCurrentBranch
{
[self selectBranch: [self addBranch: [self headRef]]];
}
- (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*) outputForCommand:(NSString *)str retValue:(int *)ret;
{
NSArray* arguments = [str componentsSeparatedByString:@" "];
return [self outputForArguments: arguments retValue: ret];
}
- (NSString*) outputForArguments:(NSArray*) arguments
{
return [PBEasyPipe outputForCommand:gitPath withArgs:arguments inDir: self.fileURL.path];
}
- (NSString*) outputForArguments:(NSArray *)arguments retValue:(int *)ret;
{
return [PBEasyPipe outputForCommand:gitPath withArgs:arguments inDir: self.fileURL.path retValue: ret];
}
- (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