diff --git a/Classes/Controllers/ApplicationController.m b/Classes/Controllers/ApplicationController.m index 8225ab685..9242d56bd 100644 --- a/Classes/Controllers/ApplicationController.m +++ b/Classes/Controllers/ApplicationController.m @@ -7,6 +7,7 @@ // #import "ApplicationController.h" +#import "PBRepositoryDocumentController.h" #import "PBGitRevisionCell.h" #import "PBGitWindowController.h" #import "PBServicesController.h" @@ -67,6 +68,20 @@ - (void)registerServices } } +- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { + NSURL *repository = [NSURL fileURLWithPath:filename]; + NSError *error = nil; + NSDocument *doc = [[PBRepositoryDocumentController sharedDocumentController] openDocumentWithContentsOfURL:repository + display:YES + error:&error]; + if (!doc) { + NSLog(@"Error opening repository \"%@\": %@", repository.path, error); + return NO; + } + + return YES; +} + - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { if(!started || [[[NSDocumentController sharedDocumentController] documents] count]) diff --git a/Classes/Controllers/PBRepositoryDocumentController.m b/Classes/Controllers/PBRepositoryDocumentController.m index 980bc87a0..55c450228 100644 --- a/Classes/Controllers/PBRepositoryDocumentController.m +++ b/Classes/Controllers/PBRepositoryDocumentController.m @@ -11,7 +11,6 @@ #import "PBGitRevList.h" #import "PBEasyPipe.h" #import "PBGitBinary.h" -#import "GitRepoFinder.h" #import diff --git a/Classes/Util/NSApplication+GitXScripting.m b/Classes/Util/NSApplication+GitXScripting.m index 4b190748c..dd54d9cb5 100644 --- a/Classes/Util/NSApplication+GitXScripting.m +++ b/Classes/Util/NSApplication+GitXScripting.m @@ -11,8 +11,8 @@ #import "PBDiffWindowController.h" #import "PBGitRepository.h" #import "PBCloneRepositoryPanel.h" - -#import +#import "PBGitBinary.h" +#import "PBEasyPipe.h" @implementation NSApplication (GitXScripting) @@ -27,6 +27,31 @@ - (void)showDiffScriptCommand:(NSScriptCommand *)command } } +- (void)performDiffScriptCommand:(NSScriptCommand *)command +{ + NSURL *repositoryURL = command.directParameter; + NSArray *diffOptions = command.arguments[@"diffOptions"]; + + diffOptions = [[NSArray arrayWithObjects:@"diff", @"--no-ext-diff", nil] arrayByAddingObjectsFromArray:diffOptions]; + + int retValue = 1; + NSString *diffOutput = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:diffOptions inDir:[repositoryURL path] retValue:&retValue]; + if (retValue) { + // if there is an error diffOutput should have the error output from git + if (diffOutput) + NSLog(@"%s\n", [diffOutput UTF8String]); + else + NSLog(@"Invalid diff command [%d]\n", retValue); + return; + } + + if (diffOutput) { + PBDiffWindowController *diffController = [[PBDiffWindowController alloc] initWithDiff:diffOutput]; + [diffController showWindow:self]; + [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; + } +} + - (void)initRepositoryScriptCommand:(NSScriptCommand *)command { NSError *error = nil; diff --git a/Classes/Util/PBEasyPipe.m b/Classes/Util/PBEasyPipe.m index 1cdd7cbcb..76bdc7056 100644 --- a/Classes/Util/PBEasyPipe.m +++ b/Classes/Util/PBEasyPipe.m @@ -93,8 +93,14 @@ + (NSString*) outputForCommand:(NSString *) cmd [inHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]]; [inHandle closeFile]; } - - [task launch]; + + @try { + [task launch]; + } + @catch (NSException *exception) { + if (ret) *ret = -1; + return nil; + } NSData* data = [handle readDataToEndOfFile]; NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; diff --git a/Classes/git/PBGitRepository.m b/Classes/git/PBGitRepository.m index 2ad0a6418..61dbacc66 100644 --- a/Classes/git/PBGitRepository.m +++ b/Classes/git/PBGitRepository.m @@ -21,7 +21,8 @@ #import "GitXScriptingConstants.h" #import "PBHistorySearchController.h" #import "PBGitRepositoryWatcher.h" -#import "GitRepoFinder.h" +#import "PBRepositoryFinder.h" +#import "PBGitSubmodule.h" #import "PBGitHistoryList.h" @@ -148,21 +149,17 @@ - (void)makeWindowControllers // if the repository is already open then this is also a good place to catch the event as the window is about to be brought forward - (void)showWindows { - NSAppleEventDescriptor *currentAppleEvent = [[NSAppleEventManager sharedAppleEventManager] currentAppleEvent]; + NSScriptCommand *command = [NSScriptCommand currentCommand]; - if (currentAppleEvent) { - NSAppleEventDescriptor *eventRecord = [currentAppleEvent paramDescriptorForKeyword:keyAEPropData]; + if (command) { + NSURL *repoURL = [command directParameter]; // on app launch there may be many repositories opening, so double check that this is the right repo - NSString *path = [[eventRecord paramDescriptorForKeyword:typeFileURL] stringValue]; - if (path) { - NSURL *workingDirectory = [NSURL URLWithString:path]; - if ([[GitRepoFinder gitDirForURL:workingDirectory] isEqual:[self fileURL]]) { - NSAppleEventDescriptor *argumentsList = [eventRecord paramDescriptorForKeyword:kGitXAEKeyArgumentsList]; - [self handleGitXScriptingArguments:argumentsList inWorkingDirectory:workingDirectory]; - - // showWindows may be called more than once during app launch so remove the CLI data after we handle the event - [currentAppleEvent removeDescriptorWithKeyword:keyAEPropData]; + if (repoURL) { + repoURL = [PBRepositoryFinder gitDirForURL:repoURL]; + if ([repoURL isEqual:self.gitURL]) { + NSArray *arguments = command.arguments[@"openOptions"]; + [self handleGitXScriptingArguments:arguments]; } } } @@ -215,6 +212,15 @@ - (NSURL *)gitURL { return self.gtRepo.gitDirectoryURL; } +- (NSURL *)workingDirectoryURL { + return self.gtRepo.fileURL; +} + +- (NSString *)workingDirectory +{ + return self.workingDirectoryURL.path; +} + - (void)forceUpdateRevisions { [revisionList forceUpdate]; @@ -566,20 +572,6 @@ - (void) readCurrentBranch self.currentBranch = [self addBranch: [self headRef]]; } -- (NSString *) workingDirectory -{ - const char* workdir = git_repository_workdir(self.gtRepo.git_repository); - if (workdir) - { - NSString* result = [[NSString stringWithUTF8String:workdir] stringByStandardizingPath]; - return result; - } - else - { - return self.fileURL.path; - } -} - #pragma mark Remotes - (NSArray *) remotes @@ -955,7 +947,7 @@ - (BOOL) deleteRef:(PBGitRef *)ref #pragma mark GitX Scripting -- (void)handleRevListArguments:(NSArray *)arguments inWorkingDirectory:(NSURL *)workingDirectory +- (void)handleRevListArguments:(NSArray *)arguments { if (![arguments count]) return; @@ -967,13 +959,13 @@ - (void)handleRevListArguments:(NSArray *)arguments inWorkingDirectory:(NSURL *) PBGitRef *refArgument = [self refForName:[arguments lastObject]]; if (refArgument) { revListSpecifier = [[PBGitRevSpecifier alloc] initWithRef:refArgument]; - revListSpecifier.workingDirectory = workingDirectory; + revListSpecifier.workingDirectory = self.workingDirectoryURL; } } if (!revListSpecifier) { revListSpecifier = [[PBGitRevSpecifier alloc] initWithParameters:arguments]; - revListSpecifier.workingDirectory = workingDirectory; + revListSpecifier.workingDirectory = self.workingDirectoryURL; } self.currentBranch = [self addBranch:revListSpecifier]; @@ -981,7 +973,7 @@ - (void)handleRevListArguments:(NSArray *)arguments inWorkingDirectory:(NSURL *) [self.windowController showHistoryView:self]; } -- (void)handleBranchFilterEventForFilter:(PBGitXBranchFilterType)filter additionalArguments:(NSMutableArray *)arguments inWorkingDirectory:(NSURL *)workingDirectory +- (void)handleBranchFilterEventForFilter:(PBGitXBranchFilterType)filter additionalArguments:(NSArray *)arguments { self.currentBranchFilter = filter; [PBGitDefaults setShowStageView:NO]; @@ -989,23 +981,13 @@ - (void)handleBranchFilterEventForFilter:(PBGitXBranchFilterType)filter addition // treat any additional arguments as a rev-list specifier if ([arguments count] > 1) { - [arguments removeObjectAtIndex:0]; - [self handleRevListArguments:arguments inWorkingDirectory:workingDirectory]; + arguments = [arguments subarrayWithRange:NSMakeRange(1, arguments.count)]; + [self handleRevListArguments:arguments]; } } -- (void)handleGitXScriptingArguments:(NSAppleEventDescriptor *)argumentsList inWorkingDirectory:(NSURL *)workingDirectory +- (void)handleGitXScriptingArguments:(NSArray *)arguments { - NSMutableArray *arguments = [NSMutableArray array]; - uint argumentsIndex = 1; // AppleEvent list descriptor's are one based - while(1) { - NSAppleEventDescriptor *arg = [argumentsList descriptorAtIndex:argumentsIndex++]; - if (arg) - [arguments addObject:[arg stringValue]]; - else - break; - } - if (![arguments count]) return; @@ -1018,22 +1000,22 @@ - (void)handleGitXScriptingArguments:(NSAppleEventDescriptor *)argumentsList inW } if ([firstArgument isEqualToString:@"--all"]) { - [self handleBranchFilterEventForFilter:kGitXAllBranchesFilter additionalArguments:arguments inWorkingDirectory:workingDirectory]; + [self handleBranchFilterEventForFilter:kGitXAllBranchesFilter additionalArguments:arguments]; return; } if ([firstArgument isEqualToString:@"--local"]) { - [self handleBranchFilterEventForFilter:kGitXLocalRemoteBranchesFilter additionalArguments:arguments inWorkingDirectory:workingDirectory]; + [self handleBranchFilterEventForFilter:kGitXLocalRemoteBranchesFilter additionalArguments:arguments]; return; } if ([firstArgument isEqualToString:@"--branch"]) { - [self handleBranchFilterEventForFilter:kGitXSelectedBranchFilter additionalArguments:arguments inWorkingDirectory:workingDirectory]; + [self handleBranchFilterEventForFilter:kGitXSelectedBranchFilter additionalArguments:arguments]; return; } // if the argument is not a known command then treat it as a rev-list specifier - [self handleRevListArguments:arguments inWorkingDirectory:workingDirectory]; + [self handleRevListArguments:arguments]; } // for the scripting bridge diff --git a/Classes/git/GitRepoFinder.h b/Classes/git/PBRepositoryFinder.h similarity index 73% rename from Classes/git/GitRepoFinder.h rename to Classes/git/PBRepositoryFinder.h index 05b87cb5a..5c0d514a5 100644 --- a/Classes/git/GitRepoFinder.h +++ b/Classes/git/PBRepositoryFinder.h @@ -1,5 +1,5 @@ // -// GitRepoFinder.h +// PBRepositoryFinder.h // GitX // // Created by Rowan James on 13/11/2012. @@ -8,7 +8,7 @@ #import -@interface GitRepoFinder : NSObject +@interface PBRepositoryFinder : NSObject + (NSURL*)workDirForURL:(NSURL*)fileURL; + (NSURL*)gitDirForURL:(NSURL*)fileURL; diff --git a/Classes/git/PBRepositoryFinder.m b/Classes/git/PBRepositoryFinder.m new file mode 100644 index 000000000..19f608906 --- /dev/null +++ b/Classes/git/PBRepositoryFinder.m @@ -0,0 +1,83 @@ +// +// PBRepositoryFinder.m +// GitX +// +// Created by Rowan James on 13/11/2012. +// +// + +#import "PBRepositoryFinder.h" + +@implementation PBRepositoryFinder + ++ (NSURL *)workDirForURL:(NSURL *)fileURL; +{ + if (!fileURL.isFileURL) { + return nil; + } + + git_repository *repo = NULL; + git_repository_open_ext(&repo, fileURL.path.UTF8String, GIT_REPOSITORY_OPEN_CROSS_FS, NULL); + if (!repo) { + return NULL; + } + + const char *workdir = git_repository_workdir(repo); + NSURL *result = nil; + if (workdir) { + result = [NSURL fileURLWithPath:[NSString stringWithUTF8String:workdir]]; + } + + git_repository_free(repo); repo = nil; + return result; +} + ++ (NSURL *)gitDirForURL:(NSURL *)fileURL +{ + if (!fileURL.isFileURL) + { + return nil; + } + git_buf path_buffer = {NULL, 0, 0}; + int gitResult = git_repository_discover(&path_buffer, + [fileURL.path UTF8String], + GIT_REPOSITORY_OPEN_CROSS_FS, + nil); + + NSData *repoPathBuffer = nil; + if (path_buffer.ptr) { + repoPathBuffer = [NSData dataWithBytes:path_buffer.ptr length:path_buffer.asize]; + git_buf_free(&path_buffer); + } + + if (gitResult == GIT_OK && repoPathBuffer.length) + { + NSString* repoPath = [NSString stringWithUTF8String:repoPathBuffer.bytes]; + BOOL isDirectory; + if ([[NSFileManager defaultManager] fileExistsAtPath:repoPath + isDirectory:&isDirectory] && isDirectory) + { + NSURL* result = [NSURL fileURLWithPath:repoPath + isDirectory:isDirectory]; + return result; + } + } + return nil; +} + ++ (NSURL *)fileURLForURL:(NSURL *)inputURL +{ + NSURL* gitDir = [self gitDirForURL:inputURL]; + if (!gitDir) { + return nil; // not a Git directory at all + } + + NSURL *workDir = [self workDirForURL:inputURL]; + if (workDir) { + return workDir; // root of this working copy or deepest submodule + } + + return gitDir; // bare repo +} + +@end diff --git a/Classes/gitx.m b/Classes/gitx.m index 701299675..720080173 100644 --- a/Classes/gitx.m +++ b/Classes/gitx.m @@ -6,12 +6,10 @@ // Copyright 2008 __MyCompanyName__. All rights reserved. // -#import "PBGitBinary.h" -#import "PBEasyPipe.h" +#import "PBRepositoryFinder.h" #import "GitXScriptingConstants.h" #import "GitX.h" #import "PBHistorySearchController.h" -#import "GitRepoFinder.h" #pragma mark Commands handled locally @@ -87,27 +85,11 @@ void usage(char const *programName) void version_info() { - NSString *version = [[[NSBundle bundleForClass:[PBGitBinary class]] infoDictionary] valueForKey:@"CFBundleVersion"]; - NSString *gitVersion = [[[NSBundle bundleForClass:[PBGitBinary class]] infoDictionary] valueForKey:@"CFBundleGitVersion"]; - printf("GitX version %s (%s)\n", [version UTF8String], [gitVersion UTF8String]); - if ([PBGitBinary path]) - printf("Using git found at %s, version %s\n", [[PBGitBinary path] UTF8String], [[PBGitBinary version] UTF8String]); - else - printf("GitX cannot find a git binary\n"); + NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; + printf("GitX version %s\n", [version UTF8String]); exit(1); } -void git_path() -{ - if (![PBGitBinary path]) - exit(101); - - NSString *path = [[PBGitBinary path] stringByDeletingLastPathComponent]; - printf("%s\n", [path UTF8String]); - exit(0); -} - - #pragma mark - #pragma mark Commands sent to GitX @@ -126,62 +108,23 @@ void handleSTDINDiff() void handleDiffWithArguments(NSURL *repositoryURL, NSArray *arguments) { - arguments = [[NSArray arrayWithObjects:@"diff", @"--no-ext-diff", nil] arrayByAddingObjectsFromArray:arguments]; - - int retValue = 1; - NSString *diffOutput = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:[repositoryURL path] retValue:&retValue]; - if (retValue) { - // if there is an error diffOutput should have the error output from git - if (diffOutput) - printf("%s\n", [diffOutput UTF8String]); - else - printf("Invalid diff command [%d]\n", retValue); - exit(3); - } - GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; - [gitXApp showDiff:diffOutput]; + [gitXApp performDiffIn:repositoryURL withOptions:arguments]; exit(0); } -void handleOpenRepository(NSURL *repositoryURL, NSMutableArray *arguments) +void handleOpenRepository(NSURL *repositoryURL, NSArray *arguments) { - // if there are command line arguments send them to GitX through an Apple Event - // the recordDescriptor will be stored in keyAEPropData inside the openDocument or openApplication event - NSAppleEventDescriptor *recordDescriptor = nil; - if ([arguments count]) { - recordDescriptor = [NSAppleEventDescriptor recordDescriptor]; - - NSAppleEventDescriptor *listDescriptor = [NSAppleEventDescriptor listDescriptor]; - uint listIndex = 1; // AppleEvent list descriptor's are one based - for (NSString *argument in arguments) - [listDescriptor insertDescriptor:[NSAppleEventDescriptor descriptorWithString:argument] atIndex:listIndex++]; - - [recordDescriptor setParamDescriptor:listDescriptor forKeyword:kGitXAEKeyArgumentsList]; - - // this is used as a double check in GitX - NSAppleEventDescriptor *url = [NSAppleEventDescriptor descriptorWithString:[repositoryURL absoluteString]]; - [recordDescriptor setParamDescriptor:url forKeyword:typeFileURL]; - } - - // use NSWorkspace to open GitX and send the arguments - // this allows the repository document to modify itself before it shows it's GUI - BOOL didOpenURLs = [[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:repositoryURL] - withAppBundleIdentifier:kGitXBundleIdentifier - options:0 - additionalEventParamDescriptor:recordDescriptor - launchIdentifiers:NULL]; - if (!didOpenURLs) { - printf("Unable to open GitX.app\n"); - exit(2); - } + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp open:repositoryURL withOptions:arguments]; + return; } void handleInit(NSURL *repositoryURL) { GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; - [gitXApp initRepository:repositoryURL]; + [gitXApp createRepository:repositoryURL]; exit(0); } @@ -283,54 +226,91 @@ void handleGitXSearch(NSURL *repositoryURL, NSMutableArray *arguments) #pragma mark - #pragma mark main +#define kGitDirPrefix @"--git-dir" + +NSURL *checkWorkingDirectoryPath(NSString *path) +{ + NSString *workingDirectory = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"]; + + // We might be looking at a filesystem path, try to standardize it + if (!([path hasPrefix:@"/"] || [path hasPrefix:@"~"])) { + path = [workingDirectory stringByAppendingPathComponent:path]; + } + path = [path stringByStandardizingPath]; + + // The path must exist and point to a directory + BOOL isDirectory = YES; + BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]; + if (!exists || !isDirectory) { + return nil; + } -#define kGitDirPrefix @"--git-dir=" + return [NSURL fileURLWithPath:path]; +} NSURL *workingDirectoryURL(NSMutableArray *arguments) { - // path to git repository has been explicitly passed? - if ([arguments count] && [[arguments objectAtIndex:0] hasPrefix:kGitDirPrefix]) { - NSString *path = [[[arguments objectAtIndex:0] substringFromIndex:[kGitDirPrefix length]] stringByStandardizingPath]; - - // the path must exist and point to a directory - BOOL isDirectory = YES; - if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory] || !isDirectory) { - if (!isDirectory) - printf("Fatal: --git-dir path does not point to a directory.\n"); - else - printf("Fatal: --git-dir path does not exist.\n"); - printf("Cannot open git repository at path: '%s'\n", [path UTF8String]); - exit(2); + NSString *workingDirectory = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"]; + + // First check our arguments for a --git-dir option + for (NSUInteger i = 0; i < [arguments count]; i++) { + NSString *argument = [arguments objectAtIndex:i]; + NSString *path = nil; + + if (![argument hasPrefix:kGitDirPrefix]) { + // That's not a --git-dir argument, don't bother + continue; + } + + BOOL isInlinePath = NO; + if ([argument hasPrefix:kGitDirPrefix @"="]) { + // We're looking at a --git-dir=, extract the argument + path = [argument substringFromIndex:[kGitDirPrefix length] + 1]; + isInlinePath = YES; + } else { + // We're looking at a --git-dir [arg], next argument is our path + path = [arguments objectAtIndex:i + 1]; } - // remove the git-dir argument - [arguments removeObjectAtIndex:0]; + NSURL *url = checkWorkingDirectoryPath(path); - // create and return corresponding NSURL - NSURL *url = [NSURL fileURLWithPath:path isDirectory:YES]; - if (!url) { - printf("Unable to create url to path: %s\n", [path UTF8String]); - exit(2); - } + // Let's check that this points to a repository + url = [PBRepositoryFinder workDirForURL:url]; + if (!url) { + NSLog(@"Fatal: --git-dir \"%@\" does not look like a valid repository.", argument); + exit(2); + } + + // Valid --git-dir found, let's drop parsed arguments + [arguments removeObjectAtIndex:i]; + if (!isInlinePath) { + [arguments removeObjectAtIndex:i]; + } - return url; + return url; } - // otherwise, determine current working directory - NSString *pwd = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"]; + // No --git-dir option, let's use the first thing that looks like a path + for (NSUInteger i = 0; i < [arguments count]; i++) { + NSString *path = [arguments objectAtIndex:i]; - NSURL* pwdURL = [NSURL fileURLWithPath:pwd]; - NSURL* repoURL = [GitRepoFinder workDirForURL:pwdURL]; - return repoURL; + // Stop processing arguments willy-nilly, we'll just give the CWD a spin. + // The user might be trying todo a `gitx log -- path` or something. + if ([path isEqualToString:@"--"]) break; -} + // Let's check that path and find the closest repository + NSURL *url = checkWorkingDirectoryPath(path); + url = [PBRepositoryFinder fileURLForURL:url]; + if (!url) continue; // Invalid path, let's ignore it -NSMutableArray *argumentsArray() -{ - NSMutableArray *arguments = [[[NSProcessInfo processInfo] arguments] mutableCopy]; - [arguments removeObjectAtIndex:0]; // url to executable path is not needed + // Valid repository found, lets' drop parsed argument + [arguments removeObjectAtIndex:i]; - return arguments; + return url; + } + + // Still no path found, let's default to our current working directory + return [PBRepositoryFinder fileURLForURL:[NSURL fileURLWithPath:workingDirectory]]; } int main(int argc, const char** argv) @@ -340,21 +320,20 @@ int main(int argc, const char** argv) usage(argv[0]); if (argc >= 2 && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v"))) version_info(); - if (argc >= 2 && !strcmp(argv[1], "--git-path")) - git_path(); - - // From here on everything needs to access git, so make sure it's installed - if (![PBGitBinary path]) { - printf("%s\n", [[PBGitBinary notFoundError] cStringUsingEncoding:NSUTF8StringEncoding]); - exit(2); - } - + if (argc >= 2 && !strcmp(argv[1], "--git-path")) { + printf("gitx now uses libgit2 to work."); + exit(1); + } + // gitx can be used to pipe diff output to be displayed in GitX if (!isatty(STDIN_FILENO) && fdopen(STDIN_FILENO, "r")) handleSTDINDiff(); - + + + NSMutableArray *arguments = [[[NSProcessInfo processInfo] arguments] mutableCopy]; + [arguments removeObjectAtIndex:0]; // url to executable path is not needed + // From this point, we require a working directory and the arguments - NSMutableArray *arguments = argumentsArray(); NSURL *wdURL = workingDirectoryURL(arguments); if (!wdURL) { diff --git a/GitX.entitlements b/GitX.entitlements index 0c67376eb..a37421b9f 100644 --- a/GitX.entitlements +++ b/GitX.entitlements @@ -1,5 +1,22 @@ - + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + net.phere.gitx-cli + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${CURRENT_PROJECT_VERSION} + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + diff --git a/GitX.h b/GitX.h index e293d0e17..a41de291a 100644 --- a/GitX.h +++ b/GitX.h @@ -8,6 +8,19 @@ @class GitXApplication, GitXDocument, GitXWindow; +enum GitXSaveOptions { + GitXSaveOptionsYes = 'yes ' /* Save the file. */, + GitXSaveOptionsNo = 'no ' /* Do not save the file. */, + GitXSaveOptionsAsk = 'ask ' /* Ask the user whether or not to save the file. */ +}; +typedef enum GitXSaveOptions GitXSaveOptions; + +enum GitXPrintingErrorHandling { + GitXPrintingErrorHandlingStandard = 'lwst' /* Standard PostScript error handling */, + GitXPrintingErrorHandlingDetailed = 'lwdt' /* print a detailed report of PostScript errors */ +}; +typedef enum GitXPrintingErrorHandling GitXPrintingErrorHandling; + /* @@ -24,11 +37,14 @@ @property (readonly) BOOL frontmost; // Is this the active application? @property (copy, readonly) NSString *version; // The version number of the application. -- (void) open:(NSArray *)x; // Open a document. -- (void) quit; // Quit the application. +- (id) open:(id)x; // Open a document. +- (void) print:(id)x withProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog; // Print a document. +- (void) quitSaving:(GitXSaveOptions)saving; // Quit the application. - (BOOL) exists:(id)x; // Verify that an object exists. +- (id) open:(id)x withOptions:(NSArray *)withOptions; // Open a document. - (void) showDiff:(NSString *)x; // Show the supplied diff output in a GitX window. -- (void) initRepository:(NSURL *)x NS_RETURNS_NOT_RETAINED; // Create a git repository at the given filesystem URL. +- (void) performDiffIn:(NSURL *)x withOptions:(NSArray *)withOptions; // Perform a diff operation in a repository. +- (void) createRepository:(NSURL *)x; // Create a git repository at the given filesystem URL. - (void) cloneRepository:(NSString *)x to:(NSURL *)to isBare:(BOOL)isBare; // Clone a repository. @end @@ -37,9 +53,11 @@ @interface GitXDocument : SBObject @property (copy, readonly) NSString *name; // Its name. +@property (readonly) BOOL modified; // Has it been modified since the last save? @property (copy, readonly) NSURL *file; // Its location on disk, if it has one. -- (void) close; // Close a document. +- (void) closeSaving:(GitXSaveOptions)saving savingIn:(NSURL *)savingIn; // Close a document. +- (void) printWithProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog; // Print a document. - (void) delete; // Delete an object. - (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy an object. - (void) moveTo:(SBObject *)to; // Move an object to a new location. @@ -63,7 +81,8 @@ @property BOOL zoomed; // Is the window zoomed right now? @property (copy, readonly) GitXDocument *document; // The document whose contents are displayed in the window. -- (void) close; // Close a document. +- (void) closeSaving:(GitXSaveOptions)saving savingIn:(NSURL *)savingIn; // Close a document. +- (void) printWithProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog; // Print a document. - (void) delete; // Delete an object. - (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy an object. - (void) moveTo:(SBObject *)to; // Move an object to a new location. diff --git a/GitX.sdef b/GitX.sdef index 445b9f0e1..7f4fd4188 100644 --- a/GitX.sdef +++ b/GitX.sdef @@ -1,28 +1,117 @@ - - + - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + @@ -31,12 +120,14 @@ + - + + @@ -47,12 +138,14 @@ - + + + @@ -70,7 +163,8 @@ - + + @@ -89,10 +183,13 @@ - + - + + + + @@ -102,12 +199,21 @@ + + + + + + + + + @@ -145,24 +251,55 @@ - - - + + + + + + + - + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + @@ -187,6 +324,9 @@ + + + diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 5301ed124..ddf2e8cb7 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -6,6 +6,20 @@ objectVersion = 46; objects = { +/* Begin PBXAggregateTarget section */ + 4D3F252B18C37D2E000922D9 /* Generate Scripting Header */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 4D3F252C18C37D2E000922D9 /* Build configuration list for PBXAggregateTarget "Generate Scripting Header" */; + buildPhases = ( + 4D3F252F18C37D5A000922D9 /* Generate Scripting Header */, + ); + dependencies = ( + ); + name = "Generate Scripting Header"; + productName = "Generate Scripting Header"; + }; +/* End PBXAggregateTarget section */ + /* Begin PBXBuildFile section */ 0A6858C711F7EA8A00AC2BE4 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A6858C611F7EA8A00AC2BE4 /* CoreServices.framework */; }; 2682AABB1929140E00271A4D /* GTOID+JavaScript.m in Sources */ = {isa = PBXBuildFile; fileRef = 2682AABA1929140E00271A4D /* GTOID+JavaScript.m */; }; @@ -125,8 +139,6 @@ 4A5D773914A9A9CC00DF6C68 /* PBSourceViewItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76DF14A9A9CC00DF6C68 /* PBSourceViewItem.m */; }; 4A5D773A14A9A9F600DF6C68 /* gitx.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768614A9A9CC00DF6C68 /* gitx.m */; }; 4A5D773B14A9A9F900DF6C68 /* gitx_askpasswd_main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768714A9A9CC00DF6C68 /* gitx_askpasswd_main.m */; }; - 4A5D773C14A9AA2F00DF6C68 /* PBGitBinary.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764F14A9A9CC00DF6C68 /* PBGitBinary.m */; }; - 4A5D773D14A9AA3700DF6C68 /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76B214A9A9CC00DF6C68 /* PBEasyPipe.m */; }; 4A5D777314A9AEB000DF6C68 /* PBSourceViewAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D776D14A9AEB000DF6C68 /* PBSourceViewAction.m */; }; 4A5D777414A9AEB000DF6C68 /* PBSourceViewBadge.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D776F14A9AEB000DF6C68 /* PBSourceViewBadge.m */; }; 4A5D777514A9AEB000DF6C68 /* PBSourceViewRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D777214A9AEB000DF6C68 /* PBSourceViewRemote.m */; }; @@ -136,8 +148,8 @@ 4A8F6B4C14A9B6C90002F4D7 /* MGScopeBar.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A8F6B4714A9B6110002F4D7 /* MGScopeBar.framework */; }; 4A90A73514A9D24300D0DA02 /* GitX.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 4A90A73414A9D24300D0DA02 /* GitX.sdef */; }; 4AAAFDDA14A010DD008FC9B5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A68AD6714A0050F006DE321 /* Sparkle.framework */; }; - 4AB057E31652652000DE751D /* GitRepoFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AB057E21652652000DE751D /* GitRepoFinder.m */; }; - 4AB057E41652652000DE751D /* GitRepoFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AB057E21652652000DE751D /* GitRepoFinder.m */; }; + 4AB057E31652652000DE751D /* PBRepositoryFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AB057E21652652000DE751D /* PBRepositoryFinder.m */; }; + 4AB057E41652652000DE751D /* PBRepositoryFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AB057E21652652000DE751D /* PBRepositoryFinder.m */; }; 4AB71FF814B7EDD400F1DFFC /* RJModalRepoSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AB71FF714B7EDD400F1DFFC /* RJModalRepoSheet.m */; }; 4AC42F7D16BFBADA007CCA3A /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A40159814067B7A00DB9C07 /* AppKit.framework */; }; 551BF176112F3F4B00265053 /* gitx_askpasswd in Resources */ = {isa = PBXBuildFile; fileRef = 551BF111112F371800265053 /* gitx_askpasswd */; }; @@ -279,6 +291,20 @@ remoteGlobalIDString = 88F05A6B16011E5400B7AD1D; remoteInfo = ObjectiveGitTests; }; + 4D3F253018C37D9A000922D9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4D3F252B18C37D2E000922D9; + remoteInfo = "Generate Scripting Header"; + }; + 4D3F253218C37D9F000922D9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4D3F252B18C37D2E000922D9; + remoteInfo = "Generate Scripting Header"; + }; 551BF174112F3F3500265053 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; @@ -531,10 +557,11 @@ 4A90A72C14A9C12F00D0DA02 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = SOURCE_ROOT; }; 4A90A73314A9D1E800D0DA02 /* GitX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitX.h; sourceTree = SOURCE_ROOT; }; 4A90A73414A9D24300D0DA02 /* GitX.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GitX.sdef; sourceTree = SOURCE_ROOT; }; - 4AB057E11652652000DE751D /* GitRepoFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitRepoFinder.h; sourceTree = ""; }; - 4AB057E21652652000DE751D /* GitRepoFinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitRepoFinder.m; sourceTree = ""; }; + 4AB057E11652652000DE751D /* PBRepositoryFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRepositoryFinder.h; sourceTree = ""; }; + 4AB057E21652652000DE751D /* PBRepositoryFinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRepositoryFinder.m; sourceTree = ""; usesTabs = 0; }; 4AB71FF614B7EDD400F1DFFC /* RJModalRepoSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RJModalRepoSheet.h; sourceTree = ""; }; 4AB71FF714B7EDD400F1DFFC /* RJModalRepoSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RJModalRepoSheet.m; sourceTree = ""; }; + 4DC1853818E6F93200E8DB8F /* Info-gitx.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-gitx.plist"; sourceTree = ""; }; 551BF111112F371800265053 /* gitx_askpasswd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gitx_askpasswd; sourceTree = BUILT_PRODUCTS_DIR; }; 643952751603EF9B00BB7AFF /* PBGitSVSubmoduleItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVSubmoduleItem.h; sourceTree = ""; }; 643952761603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVSubmoduleItem.m; sourceTree = ""; }; @@ -692,6 +719,7 @@ 4A5D75B514A9A90500DF6C68 /* source.css */, 4A5D75B714A9A90500DF6C68 /* UpdateKey.pem */, 4A5D75B914A9A90500DF6C68 /* XIBs */, + 4DC1853818E6F93200E8DB8F /* Info-gitx.plist */, ); path = Resources; sourceTree = ""; @@ -905,8 +933,8 @@ 4A5D768414A9A9CC00DF6C68 /* PBGitXProtocol.m */, 643952751603EF9B00BB7AFF /* PBGitSVSubmoduleItem.h */, 643952761603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m */, - 4AB057E11652652000DE751D /* GitRepoFinder.h */, - 4AB057E21652652000DE751D /* GitRepoFinder.m */, + 4AB057E11652652000DE751D /* PBRepositoryFinder.h */, + 4AB057E21652652000DE751D /* PBRepositoryFinder.m */, 2682AAB91929140E00271A4D /* GTOID+JavaScript.h */, 2682AABA1929140E00271A4D /* GTOID+JavaScript.m */, ); @@ -1051,11 +1079,11 @@ 8D11072E0486CEB800E47090 /* Frameworks */, F580E6BD0E73329C009E2D3F /* CopyFiles */, F5CF04A20EAE696C00D75C81 /* Copy HTML files */, - D81E15ED121CE83D00269E61 /* Scripting Bridge Header Script */, ); buildRules = ( ); dependencies = ( + 4D3F253118C37D9A000922D9 /* PBXTargetDependency */, 4A8F6B4A14A9B6B80002F4D7 /* PBXTargetDependency */, 4A68AD7114A00534006DE321 /* PBXTargetDependency */, 4A68AD7314A00534006DE321 /* PBXTargetDependency */, @@ -1078,6 +1106,7 @@ buildRules = ( ); dependencies = ( + 4D3F253318C37D9F000922D9 /* PBXTargetDependency */, 4A467EF01546D1F300F8902B /* PBXTargetDependency */, ); name = "cli tool"; @@ -1124,6 +1153,7 @@ 8D1107260486CEB800E47090 /* GitX */, 913D5E480E55644600CECEA2 /* cli tool */, 551BF110112F371800265053 /* gitx_askpasswd */, + 4D3F252B18C37D2E000922D9 /* Generate Scripting Header */, ); }; /* End PBXProject section */ @@ -1260,7 +1290,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - D81E15ED121CE83D00269E61 /* Scripting Bridge Header Script */ = { + 4D3F252F18C37D5A000922D9 /* Generate Scripting Header */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1268,14 +1298,13 @@ inputPaths = ( "$(SRCROOT)/GitX.sdef", ); - name = "Scripting Bridge Header Script "; + name = "Generate Scripting Header"; outputPaths = ( "$(SRCROOT)/GitX.h", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Build the scripting bridge header GitX.h if the GitX.sdef file changes\necho \"Trying to build scripting definitions from $CONFIGURATION_BUILD_DIR/GitX.app\"\nif [ -e \"$CONFIGURATION_BUILD_DIR/GitX.app\" ]; then\n\techo -n \"generating...\"\n\tsdef \"$CONFIGURATION_BUILD_DIR/GitX.app\" | sdp -fh --basename GitX\n\techo \" done.\"\nelse\n\techo \"warning: Scripting definitions weren't generated.\"\nfi"; - showEnvVarsInLog = 0; + shellScript = "# Build the scripting bridge header GitX.h if the GitX.sdef file changes\nsdp -fh --basename GitX $SRCROOT/GitX.sdef\n"; }; F5CF04A20EAE696C00D75C81 /* Copy HTML files */ = { isa = PBXShellScriptBuildPhase; @@ -1395,7 +1424,7 @@ 4A5D777514A9AEB000DF6C68 /* PBSourceViewRemote.m in Sources */, 4AB71FF814B7EDD400F1DFFC /* RJModalRepoSheet.m in Sources */, 643952771603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m in Sources */, - 4AB057E31652652000DE751D /* GitRepoFinder.m in Sources */, + 4AB057E31652652000DE751D /* PBRepositoryFinder.m in Sources */, 4A2125A417C0C78A00B5B582 /* NSColor+RGB.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1405,9 +1434,7 @@ buildActionMask = 2147483647; files = ( 4A5D773A14A9A9F600DF6C68 /* gitx.m in Sources */, - 4A5D773C14A9AA2F00DF6C68 /* PBGitBinary.m in Sources */, - 4A5D773D14A9AA3700DF6C68 /* PBEasyPipe.m in Sources */, - 4AB057E41652652000DE751D /* GitRepoFinder.m in Sources */, + 4AB057E41652652000DE751D /* PBRepositoryFinder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1439,6 +1466,16 @@ name = MGScopeBar.framework; targetProxy = 4A8F6B4914A9B6B80002F4D7 /* PBXContainerItemProxy */; }; + 4D3F253118C37D9A000922D9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4D3F252B18C37D2E000922D9 /* Generate Scripting Header */; + targetProxy = 4D3F253018C37D9A000922D9 /* PBXContainerItemProxy */; + }; + 4D3F253318C37D9F000922D9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4D3F252B18C37D2E000922D9 /* Generate Scripting Header */; + targetProxy = 4D3F253218C37D9F000922D9 /* PBXContainerItemProxy */; + }; 551BF175112F3F3500265053 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 551BF110112F371800265053 /* gitx_askpasswd */; @@ -1607,6 +1644,20 @@ }; name = Release; }; + 4D3F252D18C37D2E000922D9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 4D3F252E18C37D2E000922D9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; 551BF113112F371800265053 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1639,8 +1690,10 @@ 913D5E4B0E55644600CECEA2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CREATE_INFOPLIST_SECTION_IN_BINARY = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = GitX_Prefix.pch; + INFOPLIST_FILE = "Resources/Info-gitx.plist"; INSTALL_PATH = /usr/local/bin; PRODUCT_NAME = gitx; SKIP_INSTALL = YES; @@ -1650,8 +1703,10 @@ 913D5E4C0E55644600CECEA2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CREATE_INFOPLIST_SECTION_IN_BINARY = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = GitX_Prefix.pch; + INFOPLIST_FILE = "Resources/Info-gitx.plist"; INSTALL_PATH = /usr/local/bin; PRODUCT_NAME = gitx; SKIP_INSTALL = YES; @@ -1680,6 +1735,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 4D3F252C18C37D2E000922D9 /* Build configuration list for PBXAggregateTarget "Generate Scripting Header" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4D3F252D18C37D2E000922D9 /* Debug */, + 4D3F252E18C37D2E000922D9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 551BF119112F373E00265053 /* Build configuration list for PBXNativeTarget "gitx_askpasswd" */ = { isa = XCConfigurationList; buildConfigurations = (