diff --git a/.gitignore b/.gitignore index 3367a4e77..775fd83e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ -build -build/revision -*.xcodeproj/*.pbxuser -*.xcodeproj/*.perspectivev3 -*.xcodeproj/*.mode1v3 -*.xcodeproj/*.tm_build_errors -*.tmproj -Nightly.app.zip +stats/ +build/ +DerivedData/ +/release/ +.DS_Store +__pycache__/ +*.pyc diff --git a/.gitmodules b/.gitmodules index 255017267..6d8e04ab1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "libgit2"] - path = libgit2 - url = git://github.com/pieter/libgit2.git +[submodule "SyntaxHighlighter"] + path = External/SyntaxHighlighter + url = https://github.com/alexgorbatchev/SyntaxHighlighter.git diff --git a/ApplicationController.h b/ApplicationController.h deleted file mode 100644 index 1c8e5f44b..000000000 --- a/ApplicationController.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// GitTest_AppDelegate.h -// GitTest -// -// Created by Pieter de Bie on 13-06-08. -// Copyright __MyCompanyName__ 2008 . All rights reserved. -// - -#import -#import "PBGitRepository.h" - -@class PBCLIProxy; - -@interface ApplicationController : NSObject -{ - IBOutlet NSWindow *window; - IBOutlet id firstResponder; - NSPersistentStoreCoordinator *persistentStoreCoordinator; - NSManagedObjectModel *managedObjectModel; - NSManagedObjectContext *managedObjectContext; - - PBCLIProxy *cliProxy; -} -@property (retain) PBCLIProxy* cliProxy; - -- (NSPersistentStoreCoordinator *)persistentStoreCoordinator; -- (NSManagedObjectModel *)managedObjectModel; -- (NSManagedObjectContext *)managedObjectContext; - -- (IBAction)openPreferencesWindow:(id)sender; -- (IBAction)showAboutPanel:(id)sender; - -- (IBAction)installCliTool:(id)sender; - -- (IBAction)saveAction:sender; -- (IBAction) showHelp:(id) sender; - -@end diff --git a/ApplicationController.m b/ApplicationController.m deleted file mode 100644 index 3bc672474..000000000 --- a/ApplicationController.m +++ /dev/null @@ -1,339 +0,0 @@ -// -// GitTest_AppDelegate.m -// GitTest -// -// Created by Pieter de Bie on 13-06-08. -// Copyright __MyCompanyName__ 2008 . All rights reserved. -// - -#import "ApplicationController.h" -#import "PBGitRevisionCell.h" -#import "PBGitWindowController.h" -#import "PBRepositoryDocumentController.h" -#import "PBCLIProxy.h" -#import "PBServicesController.h" -#import "PBGitXProtocol.h" -#import "PBPrefsWindowController.h" -#import "PBNSURLPathUserDefaultsTransfomer.h" -#import "PBGitDefaults.h" - -@implementation ApplicationController -@synthesize cliProxy; - -- (ApplicationController*)init -{ -#ifdef DEBUG_BUILD - [NSApp activateIgnoringOtherApps:YES]; -#endif - - if(self = [super init]) { - if(![[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load]) - NSLog(@"Could not load QuickLook"); - - self.cliProxy = [PBCLIProxy new]; - } - - /* Value Transformers */ - NSValueTransformer *transformer = [[PBNSURLPathUserDefaultsTransfomer alloc] init]; - [NSValueTransformer setValueTransformer:transformer forName:@"PBNSURLPathUserDefaultsTransfomer"]; - - // Make sure the PBGitDefaults is initialized, by calling a random method - [PBGitDefaults class]; - return self; -} - -- (void)registerServices -{ - // Register URL - [NSURLProtocol registerClass:[PBGitXProtocol class]]; - - // Register the service class - PBServicesController *services = [[PBServicesController alloc] init]; - [NSApp setServicesProvider:services]; - - // Force update the services menu if we have a new services version - int serviceVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"Services Version"]; - if (serviceVersion < 2) - { - NSLog(@"Updating services menu…"); - NSUpdateDynamicServices(); - [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"Services Version"]; - } -} - -- (void)applicationDidFinishLaunching:(NSNotification*)notification -{ - [self registerServices]; - - // Only try to open a default document if there are no documents open already. - // For example, the application might have been launched by double-clicking a .git repository, - // or by dragging a folder to the app icon - if ([[[PBRepositoryDocumentController sharedDocumentController] documents] count]) - return; - - if (![[NSApplication sharedApplication] isActive]) - return; - - NSURL *url = nil; - - // Try to find the current directory, to open that as a repository - if ([PBGitDefaults openCurDirOnLaunch]) { - NSString *curPath = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"]; - if (curPath) - url = [NSURL fileURLWithPath:curPath]; - } - - // Try to open the found URL - NSError *error = nil; - if (url && [[PBRepositoryDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:YES error:&error]) - return; - - // The current directory was not enabled or could not be opened (most likely it’s not a git repository). - // show an open panel for the user to select a repository to view - if ([PBGitDefaults showOpenPanelOnLaunch]) - [[PBRepositoryDocumentController sharedDocumentController] openDocument:self]; -} - -- (void) windowWillClose: sender -{ - [firstResponder terminate: sender]; -} - -- (IBAction)openPreferencesWindow:(id)sender -{ - [[PBPrefsWindowController sharedPrefsWindowController] showWindow:nil]; -} - -- (IBAction)showAboutPanel:(id)sender -{ - NSString *gitversion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleGitVersion"]; - NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; - if (gitversion) - [dict addEntriesFromDictionary:[[NSDictionary alloc] initWithObjectsAndKeys:gitversion, @"Version", nil]]; - - #ifdef DEBUG_BUILD - [dict addEntriesFromDictionary:[[NSDictionary alloc] initWithObjectsAndKeys:@"GitX (DEBUG)", @"ApplicationName", nil]]; - #endif - - [NSApp orderFrontStandardAboutPanelWithOptions:dict]; -} - -- (IBAction)installCliTool:(id)sender; -{ - BOOL success = NO; - NSString* installationPath = @"/usr/local/bin/"; - NSString* installationName = @"gitx"; - NSString* toolPath = [[NSBundle mainBundle] pathForResource:@"gitx" ofType:@""]; - if (toolPath) { - AuthorizationRef auth; - if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) == errAuthorizationSuccess) { - char const* mkdir_arg[] = { "-p", [installationPath UTF8String], NULL}; - char const* mkdir = "/bin/mkdir"; - AuthorizationExecuteWithPrivileges(auth, mkdir, kAuthorizationFlagDefaults, (char**)mkdir_arg, NULL); - char const* arguments[] = { "-f", "-s", [toolPath UTF8String], [[installationPath stringByAppendingString: installationName] UTF8String], NULL }; - char const* helperTool = "/bin/ln"; - if (AuthorizationExecuteWithPrivileges(auth, helperTool, kAuthorizationFlagDefaults, (char**)arguments, NULL) == errAuthorizationSuccess) { - int status; - int pid = wait(&status); - if (pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0) - success = true; - else - errno = WEXITSTATUS(status); - } - - AuthorizationFree(auth, kAuthorizationFlagDefaults); - } - } - - if (success) { - [[NSAlert alertWithMessageText:@"Installation Complete" - defaultButton:nil - alternateButton:nil - otherButton:nil - informativeTextWithFormat:@"The gitx tool has been installed to %@", installationPath] runModal]; - } else { - [[NSAlert alertWithMessageText:@"Installation Failed" - defaultButton:nil - alternateButton:nil - otherButton:nil - informativeTextWithFormat:@"Installation to %@ failed", installationPath] runModal]; - } -} - -/** - Returns the support folder for the application, used to store the Core Data - store file. This code uses a folder named "GitTest" for - the content, either in the NSApplicationSupportDirectory location or (if the - former cannot be found), the system's temporary directory. - */ - -- (IBAction) showHelp:(id) sender -{ - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://gitx.frim.nl/user_manual.html"]]; -} - -- (NSString *)applicationSupportFolder { - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory(); - return [basePath stringByAppendingPathComponent:@"GitTest"]; -} - - -/** - Creates, retains, and returns the managed object model for the application - by merging all of the models found in the application bundle. - */ - -- (NSManagedObjectModel *)managedObjectModel { - - if (managedObjectModel != nil) { - return managedObjectModel; - } - - managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain]; - return managedObjectModel; -} - - -/** - Returns the persistent store coordinator for the application. This - implementation will create and return a coordinator, having added the - store for the application to it. (The folder for the store is created, - if necessary.) - */ - -- (NSPersistentStoreCoordinator *) persistentStoreCoordinator { - - if (persistentStoreCoordinator != nil) { - return persistentStoreCoordinator; - } - - NSFileManager *fileManager; - NSString *applicationSupportFolder = nil; - NSURL *url; - NSError *error; - - fileManager = [NSFileManager defaultManager]; - applicationSupportFolder = [self applicationSupportFolder]; - if ( ![fileManager fileExistsAtPath:applicationSupportFolder isDirectory:NULL] ) { - [fileManager createDirectoryAtPath:applicationSupportFolder attributes:nil]; - } - - url = [NSURL fileURLWithPath: [applicationSupportFolder stringByAppendingPathComponent: @"GitTest.xml"]]; - persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]]; - if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]){ - [[NSApplication sharedApplication] presentError:error]; - } - - return persistentStoreCoordinator; -} - - -/** - Returns the managed object context for the application (which is already - bound to the persistent store coordinator for the application.) - */ - -- (NSManagedObjectContext *) managedObjectContext { - - if (managedObjectContext != nil) { - return managedObjectContext; - } - - NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; - if (coordinator != nil) { - managedObjectContext = [[NSManagedObjectContext alloc] init]; - [managedObjectContext setPersistentStoreCoordinator: coordinator]; - } - - return managedObjectContext; -} - - -/** - Returns the NSUndoManager for the application. In this case, the manager - returned is that of the managed object context for the application. - */ - -- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window { - return [[self managedObjectContext] undoManager]; -} - - -/** - Performs the save action for the application, which is to send the save: - message to the application's managed object context. Any encountered errors - are presented to the user. - */ - -- (IBAction) saveAction:(id)sender { - - NSError *error = nil; - if (![[self managedObjectContext] save:&error]) { - [[NSApplication sharedApplication] presentError:error]; - } -} - - -/** - Implementation of the applicationShouldTerminate: method, used here to - handle the saving of changes in the application managed object context - before the application terminates. - */ - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - - NSError *error; - int reply = NSTerminateNow; - - if (managedObjectContext != nil) { - if ([managedObjectContext commitEditing]) { - if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { - - // This error handling simply presents error information in a panel with an - // "Ok" button, which does not include any attempt at error recovery (meaning, - // attempting to fix the error.) As a result, this implementation will - // present the information to the user and then follow up with a panel asking - // if the user wishes to "Quit Anyway", without saving the changes. - - // Typically, this process should be altered to include application-specific - // recovery steps. - - BOOL errorResult = [[NSApplication sharedApplication] presentError:error]; - - if (errorResult == YES) { - reply = NSTerminateCancel; - } - - else { - - int alertReturn = NSRunAlertPanel(nil, @"Could not save changes while quitting. Quit anyway?" , @"Quit anyway", @"Cancel", nil); - if (alertReturn == NSAlertAlternateReturn) { - reply = NSTerminateCancel; - } - } - } - } - - else { - reply = NSTerminateCancel; - } - } - - return reply; -} - - -/** - Implementation of dealloc, to release the retained variables. - */ - -- (void) dealloc { - - [managedObjectContext release], managedObjectContext = nil; - [persistentStoreCoordinator release], persistentStoreCoordinator = nil; - [managedObjectModel release], managedObjectModel = nil; - [super dealloc]; -} -@end diff --git a/CWQuickLook.h b/CWQuickLook.h deleted file mode 100644 index df1de9c4e..000000000 --- a/CWQuickLook.h +++ /dev/null @@ -1,85 +0,0 @@ -@interface QLPreviewPanel : NSPanel -+ (id)sharedPreviewPanel; -+ (id)_previewPanel; -+ (BOOL)isSharedPreviewPanelLoaded; -- (id)initWithContentRect:(struct _NSRect)fp8 styleMask:(unsigned int)fp24 backing:(unsigned int)fp28 defer:(BOOL)fp32; -- (id)initWithCoder:(id)fp8; -- (void)dealloc; -- (BOOL)isOpaque; -- (BOOL)canBecomeKeyWindow; -- (BOOL)canBecomeMainWindow; -- (BOOL)shouldIgnorePanelFrameChanges; -- (BOOL)isOpen; -- (void)setFrame:(struct _NSRect)fp8 display:(BOOL)fp24 animate:(BOOL)fp28; -- (id)_subEffectsForWindow:(id)fp8 itemFrame:(struct _NSRect)fp12 transitionWindow:(id *)fp28; -- (id)_scaleEffectForItemFrame:(struct _NSRect)fp8 transitionWindow:(id *)fp24; -- (void)_invertCurrentEffect; -- (struct _NSRect)_currentItemFrame; -- (void)setAutosizesAndCenters:(BOOL)fp8; -- (BOOL)autosizesAndCenters; -- (void)makeKeyAndOrderFront:(id)fp8; -- (void)makeKeyAndOrderFrontWithEffect:(int)fp8; -- (void)makeKeyAndGoFullscreenWithEffect:(int)fp8; -- (void)makeKeyAndOrderFrontWithEffect:(int)fp8 canClose:(BOOL)fp12; -- (void)_makeKeyAndOrderFrontWithEffect:(int)fp8 canClose:(BOOL)fp12 willOpen:(BOOL)fp16 toFullscreen:(BOOL)fp20; -- (int)openingEffect; -- (void)closePanel; -- (void)close; -- (void)closeWithEffect:(int)fp8; -- (void)closeWithEffect:(int)fp8 canReopen:(BOOL)fp12; -- (void)_closeWithEffect:(int)fp8 canReopen:(BOOL)fp12; -- (void)windowEffectDidTerminate:(id)fp8; -- (void)_close:(id)fp8; -- (void)sendEvent:(id)fp8; -- (void)selectNextItem; -- (void)selectPreviousItem; -- (void)setURLs:(id)fp8 currentIndex:(unsigned int)fp12 preservingDisplayState:(BOOL)fp16; -- (void)setURLs:(id)fp8 preservingDisplayState:(BOOL)fp12; -- (void)setURLs:(id)fp8; -- (id)URLs; -- (unsigned int)indexOfCurrentURL; -- (void)setIndexOfCurrentURL:(unsigned int)fp8; -- (void)setDelegate:(id)fp8; -- (id)sharedPreviewView; -- (void)setSharedPreviewView:(id)fp8; -- (void)setCyclesSelection:(BOOL)fp8; -- (BOOL)cyclesSelection; -- (void)setShowsAddToiPhotoButton:(BOOL)fp8; -- (BOOL)showsAddToiPhotoButton; -- (void)setShowsiChatTheaterButton:(BOOL)fp8; -- (BOOL)showsiChatTheaterButton; -- (void)setShowsFullscreenButton:(BOOL)fp8; -- (BOOL)showsFullscreenButton; -- (void)setShowsIndexSheetButton:(BOOL)fp8; -- (BOOL)showsIndexSheetButton; -- (void)setAutostarts:(BOOL)fp8; -- (BOOL)autostarts; -- (void)setPlaysDuringPanelAnimation:(BOOL)fp8; -- (BOOL)playsDuringPanelAnimation; -- (void)setDeferredLoading:(BOOL)fp8; -- (BOOL)deferredLoading; -- (void)setEnableDragNDrop:(BOOL)fp8; -- (BOOL)enableDragNDrop; -- (void)start:(id)fp8; -- (void)stop:(id)fp8; -- (void)setShowsIndexSheet:(BOOL)fp8; -- (BOOL)showsIndexSheet; -- (void)setShareWithiChat:(BOOL)fp8; -- (BOOL)shareWithiChat; -- (void)setPlaysSlideShow:(BOOL)fp8; -- (BOOL)playsSlideShow; -- (void)setIsFullscreen:(BOOL)fp8; -- (BOOL)isFullscreen; -- (void)setMandatoryClient:(id)fp8; -- (id)mandatoryClient; -- (void)setForcedContentTypeUTI:(id)fp8; -- (id)forcedContentTypeUTI; -- (void)setDocumentURLs:(id)fp8; -- (void)setDocumentURLs:(id)fp8 preservingDisplayState:(BOOL)fp12; -- (void)setDocumentURLs:(id)fp8 itemFrame:(struct _NSRect)fp12; -- (void)setURLs:(id)fp8 itemFrame:(struct _NSRect)fp12; -- (void)setAutoSizeAndCenterOnScreen:(BOOL)fp8; -- (void)setShowsAddToiPhoto:(BOOL)fp8; -- (void)setShowsiChatTheater:(BOOL)fp8; -- (void)setShowsFullscreen:(BOOL)fp8; -@end \ No newline at end of file diff --git a/Cartfile b/Cartfile new file mode 100644 index 000000000..206325bb1 --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "libgit2/objective-git" # still 0.x releases :( diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 000000000..efc011b20 --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "libgit2/objective-git" "0.14.1" diff --git a/Classes/Controllers/ApplicationController.h b/Classes/Controllers/ApplicationController.h new file mode 100644 index 000000000..eb752b1e4 --- /dev/null +++ b/Classes/Controllers/ApplicationController.h @@ -0,0 +1,30 @@ +// +// GitTest_AppDelegate.h +// GitTest +// +// Created by Pieter de Bie on 13-06-08. +// Copyright __MyCompanyName__ 2008 . All rights reserved. +// + +#import +#import "PBGitRepository.h" + +@class PBCloneRepositoryPanel; + +@interface ApplicationController : NSObject +{ + IBOutlet NSWindow *window; + IBOutlet id firstResponder; + + PBCloneRepositoryPanel *cloneRepositoryPanel; + bool started; +} + +- (IBAction)showAboutPanel:(id)sender; + +- (IBAction)showHelp:(id)sender; +- (IBAction)showChangeLog:(id)sender; +- (IBAction)reportAProblem:(id)sender; + +- (IBAction)showCloneRepository:(id)sender; +@end diff --git a/Classes/Controllers/ApplicationController.m b/Classes/Controllers/ApplicationController.m new file mode 100644 index 000000000..e4cfab65e --- /dev/null +++ b/Classes/Controllers/ApplicationController.m @@ -0,0 +1,203 @@ +// +// GitTest_AppDelegate.m +// GitTest +// +// Created by Pieter de Bie on 13-06-08. +// Copyright __MyCompanyName__ 2008 . All rights reserved. +// + +#import "ApplicationController.h" +#import "PBRepositoryDocumentController.h" +#import "PBGitRevisionCell.h" +#import "PBGitWindowController.h" +#import "PBServicesController.h" +#import "PBGitXProtocol.h" +#import "PBNSURLPathUserDefaultsTransfomer.h" +#import "PBGitDefaults.h" +#import "PBCloneRepositoryPanel.h" +#import "OpenRecentController.h" +#import "PBGitBinary.h" +#import "PBGitRepositoryDocument.h" +#import "PBRepositoryFinder.h" + +static OpenRecentController* recentsDialog = nil; + +@interface ApplicationController () +@end + +@implementation ApplicationController + +- (ApplicationController*)init +{ +#ifdef DEBUG_BUILD + [NSApp activateIgnoringOtherApps:YES]; +#endif + + if(!(self = [super init])) + return nil; + + /* Value Transformers */ + NSValueTransformer *transformer = [[PBNSURLPathUserDefaultsTransfomer alloc] init]; + [NSValueTransformer setValueTransformer:transformer forName:@"PBNSURLPathUserDefaultsTransfomer"]; + + // Make sure the PBGitDefaults is initialized, by calling a random method + [PBGitDefaults class]; + + started = NO; + + return self; +} + +- (void)registerServices +{ + // Register URL + [NSURLProtocol registerClass:[PBGitXProtocol class]]; + + // Register the service class + PBServicesController *services = [[PBServicesController alloc] init]; + [NSApp setServicesProvider:services]; + + // Force update the services menu if we have a new services version + NSInteger serviceVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"Services Version"]; + if (serviceVersion < 2) + { + NSLog(@"Updating services menu…"); + NSUpdateDynamicServices(); + [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"Services Version"]; + } +} + +- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames { + PBRepositoryDocumentController * controller = [PBRepositoryDocumentController sharedDocumentController]; + + NSScriptCommand *command = [NSScriptCommand currentCommand]; + for (NSString * filename in filenames) { + NSURL * repository = [NSURL fileURLWithPath:filename]; + [controller openDocumentWithContentsOfURL:repository display:YES completionHandler:^void (NSDocument *_document, BOOL documentWasAlreadyOpen, NSError *error) { + if (!_document) { + NSLog(@"Error opening repository \"%@\": %@", repository.path, error); + [controller presentError:error]; + [sender replyToOpenOrPrint:NSApplicationDelegateReplyFailure]; + } + else { + [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; + } + + if (command) { + PBGitRepositoryDocument *document = (id)_document; + NSURL *repoURL = [command directParameter]; + + // on app launch there may be many repositories opening, so double check that this is the right repo + if (repoURL) { + repoURL = [PBRepositoryFinder gitDirForURL:repoURL]; + if ([repoURL isEqual:[document.fileURL URLByAppendingPathComponent:@".git"]]) { + NSArray *arguments = command.arguments[@"openOptions"]; + [document handleGitXScriptingArguments:arguments]; + } + } + } + }]; + } +} + +- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender +{ + if(!started || [[[NSDocumentController sharedDocumentController] documents] count]) + return NO; + return YES; +} + +- (BOOL)applicationOpenUntitledFile:(NSApplication *)theApplication +{ + recentsDialog = [[OpenRecentController alloc] init]; + if ([recentsDialog.possibleResults count] > 0) + { + [recentsDialog show]; + return YES; + } + else + { + return NO; + } +} + +- (void)applicationDidFinishLaunching:(NSNotification*)notification +{ + // Make sure Git's SSH password requests get forwarded to our little UI tool: + setenv( "SSH_ASKPASS", [[[NSBundle mainBundle] pathForResource: @"gitx_askpasswd" ofType: @""] UTF8String], 1 ); + setenv( "DISPLAY", "localhost:0", 1 ); + + [self registerServices]; + started = YES; +} + +- (void) windowWillClose: sender +{ + [firstResponder terminate: sender]; +} + +//Override the default behavior +- (IBAction)openDocument:(id)sender { + NSOpenPanel* panel = [[NSOpenPanel alloc] init]; + + [panel setCanChooseFiles:false]; + [panel setCanChooseDirectories:true]; + + [panel beginWithCompletionHandler:^(NSInteger result) { + if (result == NSModalResponseOK) { + PBRepositoryDocumentController* controller = [PBRepositoryDocumentController sharedDocumentController]; + [controller openDocumentWithContentsOfURL:panel.URL display:true completionHandler:^(NSDocument * _Nullable document, BOOL documentWasAlreadyOpen, NSError * _Nullable error) { + if (!document) { + NSLog(@"Error opening repository \"%@\": %@", panel.URL.path, error); + [controller presentError:error]; + } + }]; + } + }]; +} + +- (IBAction)showAboutPanel:(id)sender +{ + NSString *gitversion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleGitVersion"]; + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + if (gitversion) + [dict addEntriesFromDictionary:[[NSDictionary alloc] initWithObjectsAndKeys:gitversion, @"Version", nil]]; + + #ifdef DEBUG_BUILD + [dict addEntriesFromDictionary:[[NSDictionary alloc] initWithObjectsAndKeys:@"GitX (DEBUG)", @"ApplicationName", nil]]; + #endif + + [dict addEntriesFromDictionary:[[NSDictionary alloc] initWithObjectsAndKeys:@"GitX (ft. Codebase)", @"ApplicationName", nil]]; + + [NSApp orderFrontStandardAboutPanelWithOptions:dict]; +} + +- (IBAction) showCloneRepository:(id)sender +{ + if (!cloneRepositoryPanel) + cloneRepositoryPanel = [PBCloneRepositoryPanel panel]; + + [cloneRepositoryPanel showWindow:self]; +} + + +#pragma mark Help menu + +- (IBAction)showHelp:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://gitx.github.io"]]; +} + +- (IBAction)reportAProblem:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://github.com/gitx/gitx/issues"]]; +} + +- (IBAction)showChangeLog:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://github.com/gitx/gitx/releases"]]; +} + + + +@end diff --git a/Classes/Controllers/OpenRecentController.h b/Classes/Controllers/OpenRecentController.h new file mode 100644 index 000000000..3a9dcc1e7 --- /dev/null +++ b/Classes/Controllers/OpenRecentController.h @@ -0,0 +1,29 @@ +// +// OpenRecentController.h +// GitX +// +// Created by Hajo Nils Krabbenhöft on 07.10.10. +// Copyright 2010 spratpix GmbH & Co. KG. All rights reserved. +// + +#import + + +@interface OpenRecentController : NSWindowController { + IBOutlet NSSearchField* searchField; + NSURL* selectedResult; + IBOutlet NSTableView* resultViewer; +} + +@property (strong) NSMutableArray* currentResults; +@property (strong) NSMutableArray* possibleResults; + +- (void) hide; +- (void) show; + +- (IBAction)doSearch:(id) sender; +- (IBAction)changeSelection:(id) sender; +- (IBAction)tableDoubleClick:(id)sender; + + +@end diff --git a/Classes/Controllers/OpenRecentController.m b/Classes/Controllers/OpenRecentController.m new file mode 100644 index 000000000..db5cf02ad --- /dev/null +++ b/Classes/Controllers/OpenRecentController.m @@ -0,0 +1,160 @@ +// +// OpenRecentController.m +// GitX +// +// Created by Hajo Nils Krabbenhöft on 07.10.10. +// Copyright 2010 spratpix GmbH & Co. KG. All rights reserved. +// + +#import "OpenRecentController.h" +#import "PBGitDefaults.h" + +@implementation OpenRecentController + +@synthesize currentResults; +@synthesize possibleResults; + +- (id)init +{ + self = [super initWithWindowNibName:@"OpenRecentPopup"]; + if (!self) + return nil; + + currentResults = [NSMutableArray array]; + + possibleResults = [NSMutableArray array]; + for (NSURL *url in [[NSDocumentController sharedDocumentController] recentDocumentURLs]) { + if ([url checkResourceIsReachableAndReturnError:NULL]) { + [possibleResults addObject: url]; + } + } + + + return self; +} + +- (void) show +{ + [self doSearch:self]; + [self.window makeKeyAndOrderFront:self]; +} + +- (void) hide +{ + [[self window] orderOut:self]; +} + +- (IBAction)doSearch:(id) sender +{ + NSString *searchString = [searchField stringValue]; + + while( [currentResults count] > 0 ) [currentResults removeLastObject]; + + for(NSURL* url in possibleResults){ + NSString* label = [url lastPathComponent]; + if([searchString length] > 0) { + NSRange aRange = [label rangeOfString: searchString options: NSCaseInsensitiveSearch]; + if (aRange.location == NSNotFound) continue; + } + [currentResults addObject: url]; + } + + if( [currentResults count] > 0 ) + selectedResult = [currentResults objectAtIndex:0]; + else + selectedResult = nil; + + [resultViewer reloadData]; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + [resultViewer setTarget:self]; + [resultViewer setDoubleAction:@selector(tableDoubleClick:)]; +} + +- (IBAction) tableDoubleClick:(id)sender +{ + [self changeSelection:self]; + if(selectedResult != nil) { + [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:selectedResult display:YES completionHandler:^(NSDocument * _Nullable document, BOOL documentWasAlreadyOpen, NSError * _Nullable error) { + + }]; + } + [self hide]; +} + +- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector { + BOOL result = NO; + if (commandSelector == @selector(insertNewline:)) { + if(selectedResult != nil) { + [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:selectedResult display:YES completionHandler:^(NSDocument * _Nullable document, BOOL documentWasAlreadyOpen, NSError * _Nullable error) { + + }]; + } + [self hide]; +// [searchWindow makeKeyAndOrderFront: nil]; + result = YES; + } + else if(commandSelector == @selector(cancelOperation:)) { + [self hide]; + result = YES; + } + else if(commandSelector == @selector(moveUp:)) { + if(selectedResult != nil) { + NSUInteger index = [currentResults indexOfObject:selectedResult] - 1; + if (index > 0) { + index -= 1; + } + selectedResult = [currentResults objectAtIndex:index]; + [resultViewer selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:FALSE]; + [resultViewer scrollRowToVisible:index]; + } + result = YES; + } + else if(commandSelector == @selector(moveDown:)) { + if(selectedResult != nil) { + NSUInteger index = [currentResults indexOfObject:selectedResult] + 1; + if(index >= [currentResults count]) index = [currentResults count] - 1; + selectedResult = [currentResults objectAtIndex:index]; + [resultViewer selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:FALSE]; + [resultViewer scrollRowToVisible:index]; + } + result = YES; + } + return result; +} + +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + id theValue = nil; + NSParameterAssert(rowIndex >= 0 && rowIndex < [currentResults count]); + + NSURL* row = [currentResults objectAtIndex:rowIndex]; + if( [[aTableColumn identifier] isEqualToString: @"icon"] ) { + id icon; + NSError* error; + [row getResourceValue:&icon forKey:NSURLEffectiveIconKey error:&error]; + return icon; + } else if( [[aTableColumn identifier] isEqualToString: @"label"] ) { + return [row lastPathComponent]; + } + return theValue; + +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView +{ + return [currentResults count]; +} + +- (IBAction)changeSelection:(id) sender { + NSInteger i = resultViewer.selectedRow; + if(i >= 0 && i < currentResults.count) + selectedResult = [currentResults objectAtIndex: i]; + else + selectedResult = nil; +} + +@end diff --git a/Classes/Controllers/PBDiffWindowController.h b/Classes/Controllers/PBDiffWindowController.h new file mode 100644 index 000000000..a906a1d28 --- /dev/null +++ b/Classes/Controllers/PBDiffWindowController.h @@ -0,0 +1,20 @@ +// +// PBDiffWindowController.h +// GitX +// +// Created by Pieter de Bie on 13-10-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import + +@class PBGitCommit; + +@interface PBDiffWindowController : NSWindowController + ++ (void)showDiff:(NSString *)diff; ++ (void)showDiffWindowWithFiles:(NSArray *)filePaths fromCommit:(PBGitCommit *)startCommit diffCommit:(PBGitCommit *)diffCommit; +- (instancetype)initWithDiff:(NSString *)diff; + +@property (readonly) NSString *diff; +@end diff --git a/Classes/Controllers/PBDiffWindowController.m b/Classes/Controllers/PBDiffWindowController.m new file mode 100644 index 000000000..8ac8d5395 --- /dev/null +++ b/Classes/Controllers/PBDiffWindowController.m @@ -0,0 +1,42 @@ +// +// PBDiffWindowController.m +// GitX +// +// Created by Pieter de Bie on 13-10-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import "PBDiffWindowController.h" +#import "PBGitRepository.h" +#import "PBGitCommit.h" +#import "PBGitDefaults.h" + + +@implementation PBDiffWindowController + ++ (void)showDiff:(NSString *)diff +{ + PBDiffWindowController *diffController = [[self alloc] initWithDiff:diff]; + [diffController showWindow:self]; +} + ++ (void)showDiffWindowWithFiles:(NSArray *)filePaths fromCommit:(PBGitCommit *)startCommit diffCommit:(PBGitCommit *)diffCommit +{ + NSParameterAssert(startCommit != nil); + NSString *diff = [startCommit.repository performDiff:startCommit against:diffCommit forFiles:filePaths]; + + [PBDiffWindowController showDiff:[diff copy]]; +} + +- (id)initWithDiff:(NSString *)aDiff +{ + self = [super initWithWindowNibName:@"PBDiffWindow"]; + if (!self) return nil; + + _diff = aDiff; + + return self; +} + + +@end diff --git a/Classes/Controllers/PBGitCommitController.h b/Classes/Controllers/PBGitCommitController.h new file mode 100644 index 000000000..f6698e713 --- /dev/null +++ b/Classes/Controllers/PBGitCommitController.h @@ -0,0 +1,23 @@ +// +// PBGitCommitController.h +// GitX +// +// Created by Pieter de Bie on 19-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBViewController.h" + +@class PBGitIndex; + +@interface PBGitCommitController : PBViewController + +- (IBAction) refresh:(id) sender; +- (IBAction) commit:(id) sender; +- (IBAction) forceCommit:(id) sender; +- (IBAction) signOff:(id)sender; + +- (PBGitIndex *) index; + +@end diff --git a/Classes/Controllers/PBGitCommitController.m b/Classes/Controllers/PBGitCommitController.m new file mode 100644 index 000000000..97d866117 --- /dev/null +++ b/Classes/Controllers/PBGitCommitController.m @@ -0,0 +1,717 @@ +// +// PBGitCommitController.m +// GitX +// +// Created by Pieter de Bie on 19-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitCommitController.h" +#import "NSFileHandleExt.h" +#import "PBChangedFile.h" +#import "PBWebChangesController.h" +#import "PBGitIndex.h" +#import "PBGitRepositoryWatcher.h" +#import "PBCommitMessageView.h" +#import "NSSplitView+GitX.h" + +#import +#import + +#define kMinimalCommitMessageLength 3 +#define kNotificationDictionaryDescriptionKey @"description" +#define kNotificationDictionaryMessageKey @"message" + +#define FileChangesTableViewType @"GitFileChangedType" + +@interface PBGitCommitController () { + IBOutlet PBCommitMessageView *commitMessageView; + + IBOutlet NSArrayController *unstagedFilesController; + IBOutlet NSArrayController *stagedFilesController; + IBOutlet NSArrayController *trackedFilesController; + + IBOutlet NSTabView *controlsTabView; + IBOutlet NSButton *commitButton; + + IBOutlet PBWebChangesController *webController; + IBOutlet NSSplitView *commitSplitView; +} + +@property (weak) IBOutlet NSTableView *unstagedTable; +@property (weak) IBOutlet NSTableView *stagedTable; + +@end + +@implementation PBGitCommitController + +@synthesize stagedTable=stagedTable; +@synthesize unstagedTable=unstagedTable; + +- (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller +{ + if (!(self = [super initWithRepository:theRepository superController:controller])) + return nil; + + PBGitIndex *index = theRepository.index; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshFinished:) name:PBGitIndexFinishedIndexRefresh object:index]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commitStatusUpdated:) name:PBGitIndexCommitStatus object:index]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commitFinished:) name:PBGitIndexFinishedCommit object:index]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commitFailed:) name:PBGitIndexCommitFailed object:index]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commitHookFailed:) name:PBGitIndexCommitHookFailed object:index]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(amendCommit:) name:PBGitIndexAmendMessageAvailable object:index]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(indexChanged:) name:PBGitIndexIndexUpdated object:index]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(indexOperationFailed:) name:PBGitIndexOperationFailed object:index]; + + return self; +} + +- (void)awakeFromNib +{ + [commitSplitView pb_restoreAutosavedPositions]; + + [super awakeFromNib]; + + commitMessageView.repository = self.repository; + commitMessageView.delegate = self; + + [unstagedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasUnstagedChanges == 1"]]; + [stagedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasStagedChanges == 1"]]; + [trackedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"status > 0"]]; + + [unstagedFilesController setSortDescriptors:[NSArray arrayWithObjects: + [[NSSortDescriptor alloc] initWithKey:@"status" ascending:false], + [[NSSortDescriptor alloc] initWithKey:@"path" ascending:true], nil]]; + [stagedFilesController setSortDescriptors:[NSArray arrayWithObject: + [[NSSortDescriptor alloc] initWithKey:@"path" ascending:true]]]; + + // listen for updates + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_repositoryUpdatedNotification:) name:PBGitRepositoryEventNotification object:repository]; + + [stagedFilesController setAutomaticallyRearrangesObjects:NO]; + [unstagedFilesController setAutomaticallyRearrangesObjects:NO]; + + [unstagedTable setDoubleAction:@selector(didDoubleClickOnTable:)]; + [stagedTable setDoubleAction:@selector(didDoubleClickOnTable:)]; + + [unstagedTable setTarget:self]; + [stagedTable setTarget:self]; + + [unstagedTable registerForDraggedTypes: [NSArray arrayWithObject:FileChangesTableViewType]]; + [stagedTable registerForDraggedTypes: [NSArray arrayWithObject:FileChangesTableViewType]]; + + // Copy the menu over so we have two discrete menu objects + // which allows us to tell them apart in our delegate methods + stagedTable.menu = [unstagedTable.menu copy]; +} + +- (void) _repositoryUpdatedNotification:(NSNotification *)notification { + PBGitRepositoryWatcherEventType eventType = [(NSNumber *)[[notification userInfo] objectForKey:kPBGitRepositoryEventTypeUserInfoKey] unsignedIntValue]; + if(eventType & (PBGitRepositoryWatcherEventTypeWorkingDirectory | PBGitRepositoryWatcherEventTypeIndex)){ + // refresh if the working directory or index is modified + [self refresh:self]; + } +} + +- (void) updateView +{ + [self refresh:nil]; +} + +- (void)closeView +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [webController closeView]; +} + +- (NSResponder *)firstResponder; +{ + return commitMessageView; +} + +- (PBGitIndex *) index { + return repository.index; +} + +- (void) commitWithVerification:(BOOL) doVerify +{ + if ([[NSFileManager defaultManager] fileExistsAtPath:[repository.gitURL.path stringByAppendingPathComponent:@"MERGE_HEAD"]]) { + NSString * message = NSLocalizedString(@"Cannot commit merges", + @"Title for sheet that GitX cannot create merge commits"); + NSString * info = NSLocalizedString(@"GitX cannot commit merges yet. Please commit your changes from the command line.", + @"Information text for sheet that GitX cannot create merge commits"); + + [self.windowController showMessageSheet:message infoText:info]; + return; + } + + if ([[stagedFilesController arrangedObjects] count] == 0) { + NSString * message = NSLocalizedString(@"No changes to commit", + @"Title for sheet that you need to stage changes before creating a commit"); + NSString * info = NSLocalizedString(@"You need to stage some changed files before committing by moving them to the list of Staged Changes.", + @"Information text for sheet that you need to stage changes before creating a commit"); + + [self.windowController showMessageSheet:message infoText:info]; + return; + } + + NSString *commitMessage = [commitMessageView string]; + if (commitMessage.length < kMinimalCommitMessageLength) { + NSString * message = NSLocalizedString(@"Missing commit message", + @"Title for sheet that you need to enter a commit message before creating a commit"); + NSString * info = [NSString stringWithFormat: + NSLocalizedString(@"Please enter a commit message at least %i characters long before commiting.", + @"Format for sheet that you need to enter a commit message before creating a commit giving the minimum length of the commit message required"), + kMinimalCommitMessageLength ]; + [self.windowController showMessageSheet:message infoText:info ]; + return; + } + + [stagedFilesController setSelectionIndexes:[NSIndexSet indexSet]]; + [unstagedFilesController setSelectionIndexes:[NSIndexSet indexSet]]; + + self.isBusy = YES; + commitMessageView.editable = NO; + + [repository.index commitWithMessage:commitMessage andVerify:doVerify]; +} + +- (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force +{ + void (^performDiscard)(NSModalResponse) = ^(NSModalResponse returnCode) { + if (returnCode != NSAlertFirstButtonReturn) return; + + [self.repository.index discardChangesForFiles:files]; + }; + + if (!force) { + NSAlert *alert = [[NSAlert alloc] init]; + alert.messageText = NSLocalizedString(@"Discard changes", @"Title for Discard Changes sheet"); + alert.informativeText = NSLocalizedString(@"Are you sure you wish to discard the changes to this file?\n\nYou cannot undo this operation.", @"Informative text for Discard Changes sheet"); + + [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button in Discard Changes sheet")]; + [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button in Discard Changes sheet")]; + + + [alert beginSheetModalForWindow:self.windowController.window completionHandler:performDiscard]; + } else { + performDiscard(NSAlertFirstButtonReturn); + } +} + +#pragma mark IBActions + +- (IBAction)signOff:(id)sender +{ + NSError *error = nil; + GTConfiguration *config = [repository.gtRepo configurationWithError:&error]; + NSString *userName = [config stringForKey:@"user.name"]; + NSString *userEmail = [config stringForKey:@"user.email"]; + if (!(userName && userEmail)) { + return [self.windowController showMessageSheet:NSLocalizedString(@"User‘s name not set", + @"Title for sheet that the user’s name is not set in the git configuration") + infoText:NSLocalizedString(@"Signing off a commit requires setting user.name and user.email in your git config", + @"Information text for sheet that the user’s name is not set in the git configuration")]; + } + + NSString *SOBline = [NSString stringWithFormat:NSLocalizedString(@"Signed-off-by: %@ <%@>", + @"Signed off message format. Most likely this should not be localised."), + userName, + userEmail]; + + if([commitMessageView.string rangeOfString:SOBline].location == NSNotFound) { + NSArray *selectedRanges = [commitMessageView selectedRanges]; + commitMessageView.string = [NSString stringWithFormat:@"%@\n\n%@", commitMessageView.string, SOBline]; + [commitMessageView setSelectedRanges:selectedRanges]; + } +} + +- (IBAction) refresh:(id) sender +{ + self.isBusy = YES; + self.status = NSLocalizedString(@"Refreshing index…", @"Message in status bar while the index is refreshing"); + [repository.index refresh]; + + // Reload refs (in case HEAD changed) + [repository reloadRefs]; +} + +- (IBAction) commit:(id) sender +{ + [self commitWithVerification:YES]; +} + +- (IBAction) forceCommit:(id) sender +{ + [self commitWithVerification:NO]; +} + +- (IBAction)toggleAmendCommit:(id)sender +{ + [[[self repository] index] setAmend:![[[self repository] index] isAmend]]; +} + +- (NSArray *)selectedFilesForSender:(id)sender +{ + NSParameterAssert(sender != nil); + + if (![sender isKindOfClass:[NSMenuItem class]]) return nil; + + NSTableView *table = (sender == stagedTable.menu ? stagedTable : unstagedTable); + NSArrayController *controller = (table.tag == 0 ? unstagedFilesController : stagedFilesController); + return controller.selectedObjects; +} + +- (IBAction)openFiles:(id)sender +{ + NSArray *selectedFiles = [self selectedFilesForSender:sender]; + + NSMutableArray *fileURLs = [NSMutableArray array]; + NSURL *workingDirectoryURL = self.repository.workingDirectoryURL; + for (PBChangedFile *file in selectedFiles) { + [fileURLs addObject:[workingDirectoryURL URLByAppendingPathComponent:file.path]]; + } + [self.windowController openURLs:fileURLs]; +} + +- (IBAction)revealInFinder:(id)sender +{ + NSArray *selectedFiles = [self selectedFilesForSender:sender]; + + NSMutableArray *fileURLs = [NSMutableArray array]; + NSURL *workingDirectoryURL = self.repository.workingDirectoryURL; + for (PBChangedFile *file in selectedFiles) { + [fileURLs addObject:[workingDirectoryURL URLByAppendingPathComponent:file.path]]; + } + [self.windowController revealURLsInFinder:fileURLs]; +} + +- (IBAction)moveToTrash:(id)sender +{ + NSArray *selectedFiles = [self selectedFilesForSender:sender]; + + NSURL *workingDirectoryURL = self.repository.workingDirectoryURL; + + NSAlert *confirmTrash = [[NSAlert alloc] init]; + confirmTrash.alertStyle = NSAlertStyleWarning; + confirmTrash.messageText = NSLocalizedString(@"Move to trash", @"Move to trash alert - title"); + confirmTrash.informativeText = NSLocalizedString(@"Do you want to move the following files to the trash ?", @"Move to trash alert - message"); + [confirmTrash addButtonWithTitle:NSLocalizedString(@"OK", @"Move to trash alert - OK button")]; + [confirmTrash addButtonWithTitle:NSLocalizedString(@"Cancel", @"Move to trash alert - Cancel button")]; + + [confirmTrash beginSheetModalForWindow:self.windowController.window completionHandler:^(NSModalResponse returnCode) { + if (returnCode != NSAlertFirstButtonReturn) return; + + BOOL anyTrashed = NO; + for (PBChangedFile *file in selectedFiles) + { + NSURL* fileURL = [workingDirectoryURL URLByAppendingPathComponent:[file path]]; + + NSError* error = nil; + NSURL* resultURL = nil; + if ([[NSFileManager defaultManager] trashItemAtURL:fileURL + resultingItemURL:&resultURL + error:&error]) + { + anyTrashed = YES; + } + } + if (anyTrashed) + { + [self.repository.index refresh]; + } + }]; +} + +- (IBAction)ignoreFiles:(id) sender +{ + NSArray *selectedFiles = [self selectedFilesForSender:sender]; + if ([selectedFiles count] == 0) + return; + + // Build selected files + NSMutableArray *fileList = [NSMutableArray array]; + for (PBChangedFile *file in selectedFiles) { + NSString *name = file.path; + if ([name length] > 0) + [fileList addObject:name]; + } + + NSError *error = nil; + BOOL success = [self.repository ignoreFilePaths:fileList error:&error]; + if (!success) { + [self.windowController showErrorSheet:error]; + } + [self.repository.index refresh]; +} + +static void reselectNextFile(NSArrayController *controller) +{ + NSUInteger currentSelectionIndex = controller.selectionIndex; + dispatch_async(dispatch_get_main_queue(), ^{ + NSUInteger newSelectionIndex = MIN(currentSelectionIndex, [controller.arrangedObjects count] - 1); + controller.selectionIndex = newSelectionIndex; + }); +} + +- (IBAction)stageFiles:(id)sender { + [self.repository.index stageFiles:unstagedFilesController.selectedObjects]; + reselectNextFile(unstagedFilesController); +} + +- (IBAction)unstageFiles:(id)sender { + [self.repository.index unstageFiles:stagedFilesController.selectedObjects]; + reselectNextFile(stagedFilesController); +} + +- (IBAction)discardFiles:(id)sender +{ + NSArray *selectedFiles = unstagedFilesController.selectedObjects; + if ([selectedFiles count] > 0) + [self discardChangesForFiles:selectedFiles force:FALSE]; +} + +- (IBAction)discardFilesForcibly:(id)sender +{ + NSArray *selectedFiles = unstagedFilesController.selectedObjects; + if ([selectedFiles count] > 0) + [self discardChangesForFiles:selectedFiles force:TRUE]; +} + +# pragma mark PBGitIndex Notification handling + +- (void)refreshFinished:(NSNotification *)notification +{ + self.isBusy = NO; + self.status = NSLocalizedString(@"Index refresh finished", @"Message in status bar when refreshing the index is done"); +} + +- (void)commitStatusUpdated:(NSNotification *)notification +{ + self.status = notification.userInfo[kNotificationDictionaryDescriptionKey]; +} + +- (void)commitFinished:(NSNotification *)notification +{ + commitMessageView.editable = YES; + commitMessageView.string = @""; + [webController setStateMessage:notification.userInfo[kNotificationDictionaryDescriptionKey]]; +} + +- (void)commitFailed:(NSNotification *)notification +{ + self.isBusy = NO; + commitMessageView.editable = YES; + + NSString *reason = notification.userInfo[kNotificationDictionaryDescriptionKey]; + self.status = [NSString stringWithFormat: + NSLocalizedString(@"Commit failed: %@", + @"Message in status bar when creating a commit has failed, including the reason for the failure"), + reason]; + [self.windowController showMessageSheet:NSLocalizedString(@"Commit failed", @"Title for sheet that creating a commit has failed") + infoText:reason]; +} + +- (void)commitHookFailed:(NSNotification *)notification +{ + self.isBusy = NO; + commitMessageView.editable = YES; + + NSString *reason = notification.userInfo[kNotificationDictionaryDescriptionKey]; + self.status = [NSString stringWithFormat: + NSLocalizedString(@"Commit hook failed: %@", + @"Message in status bar when running a commit hook failed, including the reason for the failure"), + reason]; + [self.windowController showCommitHookFailedSheet:NSLocalizedString(@"Commit hook failed", @"Title for sheet that running a commit hook has failed") + infoText:reason + commitController:self]; +} + +- (void)amendCommit:(NSNotification *)notification +{ + // Replace commit message with the old one if it's less than 3 characters long. + // This is just a random number. + if ([[commitMessageView string] length] > kMinimalCommitMessageLength) { + return; + } + + NSString *message = notification.userInfo[kNotificationDictionaryMessageKey]; + commitMessageView.string = message; +} + +- (void)indexChanged:(NSNotification *)notification +{ + [stagedFilesController rearrangeObjects]; + [unstagedFilesController rearrangeObjects]; + + commitButton.enabled = ([[stagedFilesController arrangedObjects] count] > 0); +} + +- (void)indexOperationFailed:(NSNotification *)notification +{ + [self.windowController showMessageSheet:NSLocalizedString(@"Index operation failed", @"Title for sheet that running an index operation has failed") + infoText:notification.userInfo[kNotificationDictionaryDescriptionKey]]; +} + +#pragma mark NSTextView delegate methods + +- (void)focusTable:(NSTableView *)table +{ + if ([table numberOfRows] > 0) { + if ([table numberOfSelectedRows] == 0) { + [table selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + } + [[table window] makeFirstResponder:table]; + } +} + +- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector; +{ + if (commandSelector == @selector(insertTab:)) { + [self focusTable:stagedTable]; + return YES; + } else if (commandSelector == @selector(insertBacktab:)) { + [self focusTable:unstagedTable]; + return YES; + } + return NO; +} + +#pragma mark NSMenu delegate + +NSString *PBLocalizedStringForArray(NSArray *array, NSString *singleFormat, NSString *multipleFormat, NSString *defaultString) +{ + if (array.count == 0) { + return defaultString; + } + else if (array.count == 1) { + return [NSString stringWithFormat:singleFormat, array.firstObject.path.lastPathComponent]; + } + return [NSString stringWithFormat:multipleFormat, array.count]; +} + +BOOL canDiscardAnyFileIn(NSArray *files) +{ + for (PBChangedFile *file in files) + { + if (file.hasUnstagedChanges) + { + return YES; + } + } + return NO; +} + +BOOL shouldTrashInsteadOfDiscardAnyFileIn(NSArray *files) +{ + for (PBChangedFile *file in files) + { + if (file.status != NEW) + { + return NO; + } + } + return YES; +} + +- (void)menuNeedsUpdate:(NSMenu *)menu { + for (NSMenuItem *item in menu.itemArray) { + [self validateMenuItem:item]; + } +} + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + NSTableView *table = (menuItem.menu == stagedTable.menu ? stagedTable : unstagedTable); + NSArray *filesForStaging = unstagedFilesController.selectedObjects; + NSArray *filesForUnstaging = stagedFilesController.selectedObjects; + NSArray *selectedFiles = (table.tag == 0 ? filesForStaging : filesForUnstaging); + BOOL isInContextualMenu = (menuItem.parentItem == nil); + + if (menuItem.action == @selector(stageFiles:)) { + if (isInContextualMenu) { + menuItem.title = PBLocalizedStringForArray(filesForStaging, + NSLocalizedString(@"Stage “%@”", @"Stage file menu item (single file with name)"), + NSLocalizedString(@"Stage %i Files", @"Stage file menu item (multiple files with number)"), + NSLocalizedString(@"Stage", @"Stage file menu item (empty selection)")); + + menuItem.hidden = (filesForStaging.count == 0); + } + return filesForStaging.count > 0; + } + else if (menuItem.action == @selector(unstageFiles:)) { + if (isInContextualMenu) { + menuItem.title = PBLocalizedStringForArray(filesForUnstaging, + NSLocalizedString(@"Unstage “%@”", @"Unstage file menu item (single file with name)"), + NSLocalizedString(@"Unstage %i Files", @"Unstage file menu item (multiple files with number)"), + NSLocalizedString(@"Unstage", @"Unstage file menu item (empty selection)")); + + menuItem.hidden = (filesForUnstaging.count == 0); + } + return filesForUnstaging.count > 0; + } + else if (menuItem.action == @selector(discardFiles:)) { + if (isInContextualMenu) { + menuItem.title = PBLocalizedStringForArray(filesForStaging, + NSLocalizedString(@"Discard changes to “%@”…", @"Discard changes menu item (single file with name)"), + NSLocalizedString(@"Discard changes to %i Files…", @"Discard changes menu item (multiple files with number)"), + NSLocalizedString(@"Discard…", @"Discard changes menu item (empty selection)")); + + menuItem.hidden = shouldTrashInsteadOfDiscardAnyFileIn(filesForStaging); + } + return filesForStaging.count > 0 && canDiscardAnyFileIn(filesForStaging); + } + else if (menuItem.action == @selector(discardFilesForcibly:)) { + if (isInContextualMenu) { + menuItem.title = PBLocalizedStringForArray(filesForStaging, + NSLocalizedString(@"Discard changes to “%@”", @"Force Discard changes menu item (single file with name)"), + NSLocalizedString(@"Discard changes to %i Files", @"Force Discard changes menu item (multiple files with number)"), + NSLocalizedString(@"Discard", @"Force Discard changes menu item (empty selection)")); + BOOL shouldHide = shouldTrashInsteadOfDiscardAnyFileIn(filesForStaging); + menuItem.hidden = shouldHide; + // NSMenu does not seem to hide alternative items properly: only activate the alternative seeing when menu item is shown. + menuItem.alternate = !shouldHide; + } + return filesForStaging.count > 0 && canDiscardAnyFileIn(filesForStaging); + } + else if (menuItem.action == @selector(trashFiles:)) { + if (isInContextualMenu) { + menuItem.title = PBLocalizedStringForArray(filesForStaging, + NSLocalizedString(@"Move “%@” to Trash", @"Move to Trash menu item (single file with name)"), + NSLocalizedString(@"Move %i Files to Trash", @"Move to Trash menu item (multiple files with number)"), + NSLocalizedString(@"Move to Trash", @"Move to Trash menu item (empty selection)")); + BOOL isVisible = shouldTrashInsteadOfDiscardAnyFileIn(filesForStaging) && table.tag != 1; + menuItem.hidden = !isVisible; + } + return filesForStaging.count > 0 && canDiscardAnyFileIn(filesForStaging); + } + else if (menuItem.action == @selector(openFiles:)) { + if (selectedFiles.count == 0) return NO; + + NSString *filePath = selectedFiles.firstObject.path; + if (isInContextualMenu) { + if (selectedFiles.count == 1 && [self.repository submoduleAtPath:filePath error:NULL] != nil) { + menuItem.title = [NSString stringWithFormat:NSLocalizedString(@"Open Submodule “%@” in GitX", @"Open Submodule Repository in GitX menu item (single file with name)"), + filePath.stringByStandardizingPath]; + } else { + menuItem.title = PBLocalizedStringForArray(selectedFiles, + NSLocalizedString(@"Open “%@”", @"Open File menu item (single file with name)"), + NSLocalizedString(@"Open %i Files", @"Open File menu item (multiple files with number)"), + NSLocalizedString(@"Open", @"Open File menu item (empty selection)")); + } + } + return YES; + } + else if (menuItem.action == @selector(ignoreFiles:)) { + BOOL isActive = selectedFiles.count > 0 && table.tag == 0; + if (isInContextualMenu) { + menuItem.title = PBLocalizedStringForArray(selectedFiles, + NSLocalizedString(@"Ignore “%@”", @"Ignore File menu item (single file with name)"), + NSLocalizedString(@"Ignore %i Files", @"Ignore File menu item (multiple files with number)"), + NSLocalizedString(@"Ignore", @"Ignore File menu item (empty selection)")); + menuItem.hidden = !isActive; + } + return isActive; + } + else if (menuItem.action == @selector(revealInFinder:)) { + BOOL active = selectedFiles.count == 1; + if (isInContextualMenu) { + if (active) { + menuItem.title = [NSString stringWithFormat:NSLocalizedString(@"Reveal “%@” in Finder", @"Reveal File in Finder contextual menu item (single file with name)"), + selectedFiles.firstObject.path.lastPathComponent]; + } else { + menuItem.title = NSLocalizedString(@"Reveal in Finder", @"Reveal File in Finder contextual menu item (empty selection)"); + } + menuItem.hidden = !active; + } + return active; + } + else if (menuItem.action == @selector(toggleAmendCommit:)) { + menuItem.state = [[[self repository] index] isAmend] ? NSOnState : NSOffState; + return YES; + } + + return menuItem.enabled; +} + +#pragma mark PBFileChangedTableView delegate + +- (void)tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)rowIndex +{ + id controller = [tableView tag] == 0 ? unstagedFilesController : stagedFilesController; + [[tableColumn dataCell] setImage:[[[controller arrangedObjects] objectAtIndex:rowIndex] icon]]; +} + +- (void) didDoubleClickOnTable:(NSTableView *) tableView +{ + NSArrayController *controller = [tableView tag] == 0 ? unstagedFilesController : stagedFilesController; + + NSIndexSet *selectionIndexes = [tableView selectedRowIndexes]; + NSArray *files = [[controller arrangedObjects] objectsAtIndexes:selectionIndexes]; + if ([tableView tag] == 0) { + [self.index stageFiles:files]; + } + else { + [self.index unstageFiles:files]; + } +} + +- (BOOL) tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard +{ + // Copy the row numbers to the pasteboard. + [pboard declareTypes:[NSArray arrayWithObjects:FileChangesTableViewType, NSFilenamesPboardType, nil] owner:self]; + + // Internal, for dragging from one tableview to the other + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; + [pboard setData:data forType:FileChangesTableViewType]; + + // External, to drag them to for example XCode or Textmate + NSArrayController *controller = [tv tag] == 0 ? unstagedFilesController : stagedFilesController; + NSArray *files = [controller.arrangedObjects objectsAtIndexes:rowIndexes]; + NSURL *workingDirectoryURL = self.repository.workingDirectoryURL; + + NSMutableArray *URLs = [NSMutableArray arrayWithCapacity:rowIndexes.count]; + for (PBChangedFile *file in files) { + [URLs addObject:[workingDirectoryURL URLByAppendingPathComponent:file.path]]; + } + [pboard writeObjects:URLs]; + + return YES; +} + +- (NSDragOperation)tableView:(NSTableView*)tableView + validateDrop:(id )info + proposedRow:(NSInteger)row + proposedDropOperation:(NSTableViewDropOperation)operation +{ + if ([info draggingSource] == tableView) + return NSDragOperationNone; + + [tableView setDropRow:-1 dropOperation:NSTableViewDropOn]; + return NSDragOperationCopy; +} + +- (BOOL)tableView:(NSTableView *)aTableView + acceptDrop:(id )info + row:(NSInteger)row + dropOperation:(NSTableViewDropOperation)operation +{ + NSPasteboard* pboard = [info draggingPasteboard]; + NSData* rowData = [pboard dataForType:FileChangesTableViewType]; + NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData]; + + NSArrayController *controller = [aTableView tag] == 0 ? stagedFilesController : unstagedFilesController; + NSArray *files = [[controller arrangedObjects] objectsAtIndexes:rowIndexes]; + + if ([aTableView tag] == 0) { + [self.index unstageFiles:files]; + } + else { + [self.index stageFiles:files]; + } + + return YES; +} + +@end diff --git a/Classes/Controllers/PBGitHistoryController.h b/Classes/Controllers/PBGitHistoryController.h new file mode 100644 index 000000000..3fe43ad15 --- /dev/null +++ b/Classes/Controllers/PBGitHistoryController.h @@ -0,0 +1,89 @@ +// +// PBGitHistoryView.h +// GitX +// +// Created by Pieter de Bie on 19-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBViewController.h" + +@class PBGitCommit; +@class PBGitTree; + +@class PBGitSidebarController; +@class PBWebHistoryController; +@class PBGitGradientBarView; +@class PBRefController; +@class PBCommitList; +@class GLFileView; +@class GTOID; +@class PBHistorySearchController; + +@interface PBGitHistoryController : PBViewController { + IBOutlet NSArrayController *commitController; + IBOutlet NSTreeController *treeController; + IBOutlet PBWebHistoryController *webHistoryController; + IBOutlet GLFileView *fileView; + IBOutlet PBRefController *refController; + IBOutlet PBHistorySearchController *searchController; + + __weak IBOutlet NSSearchField *searchField; + __weak IBOutlet NSOutlineView *fileBrowser; + __weak IBOutlet PBCommitList *commitList; + __weak IBOutlet NSSplitView *historySplitView; + __weak IBOutlet PBGitGradientBarView *upperToolbarView; + __weak IBOutlet PBGitGradientBarView *scopeBarView; + __weak IBOutlet NSButton *allBranchesFilterItem; + __weak IBOutlet NSButton *localRemoteBranchesFilterItem; + __weak IBOutlet NSButton *selectedBranchFilterItem; + __weak IBOutlet id webView; + + NSArray *currentFileBrowserSelectionPath; + NSInteger selectedCommitDetailsIndex; + BOOL forceSelectionUpdate; + PBGitTree *gitTree; + NSArray *webCommits; + NSArray *selectedCommits; +} + +@property (readonly) NSArrayController *commitController; +@property (readonly) NSTreeController *treeController; +@property (readonly) PBRefController *refController; +@property (readonly) PBHistorySearchController *searchController; + +@property (assign) NSInteger selectedCommitDetailsIndex; +@property PBGitTree* gitTree; +@property NSArray *webCommits; +@property NSArray *selectedCommits; + +@property (readonly) PBCommitList *commitList; +@property (readonly) BOOL singleCommitSelected; +@property (readonly) BOOL singleNonHeadCommitSelected; + +- (IBAction) setDetailedView:(id)sender; +- (IBAction) setTreeView:(id)sender; +- (IBAction) setBranchFilter:(id)sender; + +- (void)selectCommit:(GTOID *)commit; +- (IBAction) refresh:(id)sender; +- (IBAction) toggleQLPreviewPanel:(id)sender; +- (IBAction) openSelectedFile:(id)sender; + +// Context menu methods +- (NSMenu *)contextMenuForTreeView; +- (NSArray *)menuItemsForPaths:(NSArray *)paths; +- (void)showCommitsFromTree:(id)sender; + +// Find/Search methods +- (void)setHistorySearch:(NSString *)searchString mode:(PBHistorySearchMode)mode; +- (IBAction)selectNext:(id)sender; +- (IBAction)selectPrevious:(id)sender; + + +- (BOOL) hasNonlinearPath; + +- (NSMenu *)tableColumnMenu; + +@end diff --git a/Classes/Controllers/PBGitHistoryController.m b/Classes/Controllers/PBGitHistoryController.m new file mode 100644 index 000000000..de3c945c5 --- /dev/null +++ b/Classes/Controllers/PBGitHistoryController.m @@ -0,0 +1,681 @@ +// +// PBGitHistoryView.m +// GitX +// +// Created by Pieter de Bie on 19-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + +#import "PBGitHistoryController.h" +#import "PBGitTree.h" +#import "PBGitRef.h" +#import "PBGitHistoryList.h" +#import "PBGitRevSpecifier.h" +#import "PBWebHistoryController.h" +#import "PBCommitList.h" +#import "PBGitGradientBarView.h" +#import "PBDiffWindowController.h" +#import "PBGitDefaults.h" +#import "PBHistorySearchController.h" +#import "PBGitRepositoryWatcher.h" +#import "PBQLTextView.h" +#import "GLFileView.h" +#import "GitXCommitCopier.h" +#import "NSSplitView+GitX.h" + +#define kHistorySelectedDetailIndexKey @"PBHistorySelectedDetailIndex" +#define kHistoryDetailViewIndex 0 +#define kHistoryTreeViewIndex 1 + +@interface PBGitHistoryController () + +- (void) updateBranchFilterMatrix; +- (void) restoreFileBrowserSelection; +- (void) saveFileBrowserSelection; + +@end + + +@implementation PBGitHistoryController +@synthesize webCommits, gitTree, commitController, refController; +@synthesize searchController; +@synthesize commitList; +@synthesize treeController; +@synthesize selectedCommits; + +- (void)awakeFromNib +{ + [historySplitView pb_restoreAutosavedPositions]; + + self.selectedCommitDetailsIndex = [[NSUserDefaults standardUserDefaults] integerForKey:kHistorySelectedDetailIndexKey]; + + [commitController addObserver:self forKeyPath:@"selection" options:0 context:@"commitChange"]; + [commitController addObserver:self forKeyPath:@"arrangedObjects.@count" options:NSKeyValueObservingOptionInitial context:@"updateCommitCount"]; + [treeController addObserver:self forKeyPath:@"selection" options:0 context:@"treeChange"]; + + [repository.revisionList addObserver:self forKeyPath:@"isUpdating" options:0 context:@"revisionListUpdating"]; + [repository addObserver:self forKeyPath:@"currentBranch" options:0 context:@"branchChange"]; + [repository addObserver:self forKeyPath:@"refs" options:0 context:@"updateRefs"]; + [repository addObserver:self forKeyPath:@"currentBranchFilter" options:0 context:@"branchFilterChange"]; + + forceSelectionUpdate = YES; + NSSize cellSpacing = [commitList intercellSpacing]; + cellSpacing.height = 0; + [commitList setIntercellSpacing:cellSpacing]; + [fileBrowser setTarget:self]; + [fileBrowser setDoubleAction:@selector(openSelectedFile:)]; + + if (!repository.currentBranch) { + [repository reloadRefs]; + [repository readCurrentBranch]; + } + else + [repository lazyReload]; + + if (![repository hasSVNRemote]) + { + // Remove the SVN revision table column for repositories with no SVN remote configured + [commitList removeTableColumn:[commitList tableColumnWithIdentifier:@"GitSVNRevision"]]; + } + + // Set a sort descriptor for the subject column in the history list, as + // It can't be sorted by default (because it's bound to a PBGitCommit) + [[commitList tableColumnWithIdentifier:@"SubjectColumn"] setSortDescriptorPrototype:[[NSSortDescriptor alloc] initWithKey:@"subject" ascending:YES]]; + // Add a menu that allows a user to select which columns to view + [[commitList headerView] setMenu:[self tableColumnMenu]]; + + [upperToolbarView setTopShade:237/255.0f bottomShade:216/255.0f]; + [scopeBarView setTopColor:[NSColor colorWithCalibratedHue:0.579 saturation:0.068 brightness:0.898 alpha:1.000] + bottomColor:[NSColor colorWithCalibratedHue:0.579 saturation:0.119 brightness:0.765 alpha:1.000]]; + [self updateBranchFilterMatrix]; + + // listen for updates + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_repositoryUpdatedNotification:) name:PBGitRepositoryEventNotification object:repository]; + + __unsafe_unretained PBGitHistoryController *weakSelf = self; + commitList.findPanelActionBlock = ^(id sender) { + [weakSelf.view.window makeFirstResponder:weakSelf->searchField]; + }; + + [super awakeFromNib]; +} + +- (void) _repositoryUpdatedNotification:(NSNotification *)notification { + PBGitRepositoryWatcherEventType eventType = [(NSNumber *)[[notification userInfo] objectForKey:kPBGitRepositoryEventTypeUserInfoKey] unsignedIntValue]; + if(eventType & PBGitRepositoryWatcherEventTypeGitDirectory){ + // refresh if the .git repository is modified + [self refresh:NULL]; + } +} + +- (void) updateKeys +{ + NSArray *newSelectedCommits = commitController.selectedObjects; + if (![self.selectedCommits isEqualToArray:newSelectedCommits]) { + self.selectedCommits = newSelectedCommits; + } + + PBGitCommit *firstSelectedCommit = self.selectedCommits.firstObject; + + if (self.selectedCommitDetailsIndex == kHistoryTreeViewIndex) { + self.gitTree = firstSelectedCommit.tree; + [self restoreFileBrowserSelection]; + } + else { + // kHistoryDetailViewIndex + if (![self.webCommits isEqualToArray:self.selectedCommits]) { + self.webCommits = self.selectedCommits; + } + } +} + +- (BOOL) singleCommitSelected +{ + return self.selectedCommits.count == 1; +} + ++ (NSSet *) keyPathsForValuesAffectingSingleCommitSelected { + return [NSSet setWithObjects:@"selectedCommits", nil]; +} + +- (BOOL) singleNonHeadCommitSelected +{ + return self.singleCommitSelected + && ![self.selectedCommits.firstObject isOnHeadBranch]; +} + ++ (NSSet *) keyPathsForValuesAffectingSingleNonHeadCommitSelected { + return [self keyPathsForValuesAffectingSingleCommitSelected]; +} + +- (void) updateBranchFilterMatrix +{ + if ([repository.currentBranch isSimpleRef]) { + [allBranchesFilterItem setEnabled:YES]; + [localRemoteBranchesFilterItem setEnabled:YES]; + + NSInteger filter = repository.currentBranchFilter; + [allBranchesFilterItem setState:(filter == kGitXAllBranchesFilter)]; + [localRemoteBranchesFilterItem setState:(filter == kGitXLocalRemoteBranchesFilter)]; + [selectedBranchFilterItem setState:(filter == kGitXSelectedBranchFilter)]; + } + else { + [allBranchesFilterItem setState:NO]; + [localRemoteBranchesFilterItem setState:NO]; + + [allBranchesFilterItem setEnabled:NO]; + [localRemoteBranchesFilterItem setEnabled:NO]; + + [selectedBranchFilterItem setState:YES]; + } + + [selectedBranchFilterItem setTitle:[repository.currentBranch title]]; + [selectedBranchFilterItem sizeToFit]; + + [localRemoteBranchesFilterItem setTitle:[[repository.currentBranch ref] isRemote] + ? NSLocalizedString(@"Remote", @"Filter button for all remote commits in history view") + : NSLocalizedString(@"Local", @"Filter button for all local commits in history view")]; +} + +- (PBGitCommit *) firstCommit +{ + NSArray *arrangedObjects = [commitController arrangedObjects]; + if ([arrangedObjects count] > 0) + return [arrangedObjects objectAtIndex:0]; + + return nil; +} + +- (BOOL)isCommitSelected +{ + return [self.selectedCommits isEqualToArray:[commitController selectedObjects]]; +} + +- (void) setSelectedCommitDetailsIndex:(NSInteger)detailsIndex +{ + if (selectedCommitDetailsIndex == detailsIndex) + return; + + selectedCommitDetailsIndex = detailsIndex; + [[NSUserDefaults standardUserDefaults] setInteger:selectedCommitDetailsIndex forKey:kHistorySelectedDetailIndexKey]; + forceSelectionUpdate = YES; + [self updateKeys]; +} + +- (NSInteger) selectedCommitDetailsIndex +{ + return selectedCommitDetailsIndex; +} + +- (void) updateStatus +{ + self.isBusy = repository.revisionList.isUpdating; + self.status = [NSString stringWithFormat:@"%lu commits loaded", [[commitController arrangedObjects] count]]; +} + +- (void) restoreFileBrowserSelection +{ + if (self.selectedCommitDetailsIndex != kHistoryTreeViewIndex) + return; + + NSArray *children = [treeController content]; + if ([children count] == 0) + return; + + NSIndexPath *path = [[NSIndexPath alloc] init]; + if ([currentFileBrowserSelectionPath count] == 0) + path = [path indexPathByAddingIndex:0]; + else { + for (NSString *pathComponent in currentFileBrowserSelectionPath) { + PBGitTree *child = nil; + NSUInteger childIndex = 0; + for (child in children) { + if ([child.path isEqualToString:pathComponent]) { + path = [path indexPathByAddingIndex:childIndex]; + children = child.children; + break; + } + childIndex++; + } + if (!child) + return; + } + } + + [treeController setSelectionIndexPath:path]; +} + +- (void) saveFileBrowserSelection +{ + NSArray *objects = [treeController selectedObjects]; + NSArray *content = [treeController content]; + + if ([objects count] && [content count]) { + PBGitTree *treeItem = [objects objectAtIndex:0]; + currentFileBrowserSelectionPath = [treeItem.fullPath componentsSeparatedByString:@"/"]; + } +} + +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + NSString* strContext = (__bridge NSString*)context; + if ([strContext isEqualToString: @"commitChange"]) { + [self updateKeys]; + [self restoreFileBrowserSelection]; + return; + } + + if ([strContext isEqualToString: @"treeChange"]) { + [self saveFileBrowserSelection]; + return; + } + + if([strContext isEqualToString:@"branchChange"]) { + // Reset the sorting + if ([[commitController sortDescriptors] count]) + [commitController setSortDescriptors:[NSArray array]]; + [self updateBranchFilterMatrix]; + return; + } + + if([strContext isEqualToString:@"updateRefs"]) { + [commitController rearrangeObjects]; + return; + } + + if ([strContext isEqualToString:@"branchFilterChange"]) { + [PBGitDefaults setBranchFilter:repository.currentBranchFilter]; + [self updateBranchFilterMatrix]; + return; + } + + if([strContext isEqualToString:@"updateCommitCount"] || [(__bridge NSString *)context isEqualToString:@"revisionListUpdating"]) { + [self updateStatus]; + + if ([repository.currentBranch isSimpleRef]) + [self selectCommit:[repository OIDForRef:repository.currentBranch.ref]]; + else + [self selectCommit:self.firstCommit.OID]; + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (IBAction) openSelectedFile:(id)sender +{ + NSArray* selectedFiles = [treeController selectedObjects]; + if ([selectedFiles count] == 0) + return; + PBGitTree* tree = [selectedFiles objectAtIndex:0]; + NSString* name = [tree tmpFileNameForContents]; + [[NSWorkspace sharedWorkspace] openFile:name]; +} + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + SEL action = menuItem.action; + + if (action == @selector(setDetailedView:)) { + [menuItem setState:(self.selectedCommitDetailsIndex == kHistoryDetailViewIndex) ? NSOnState : NSOffState]; + } else if (action == @selector(setTreeView:)) { + [menuItem setState:(self.selectedCommitDetailsIndex == kHistoryTreeViewIndex) ? NSOnState : NSOffState]; + } + + if ([self respondsToSelector:action]) { + if (action == @selector(createBranch:) || action == @selector(createTag:)) { + return self.singleCommitSelected; + } + + return YES; + } + + if (action == @selector(copy:) + || action == @selector(copySHA:) + || action == @selector(copyShortName:) + || action == @selector(copyPatch:)) { + return self.commitController.selectedObjects.count > 0; + } + + return [[self nextResponder] validateMenuItem:menuItem]; +} + +- (IBAction) setDetailedView:(id)sender +{ + self.selectedCommitDetailsIndex = kHistoryDetailViewIndex; + forceSelectionUpdate = YES; +} + +- (IBAction) setTreeView:(id)sender +{ + self.selectedCommitDetailsIndex = kHistoryTreeViewIndex; + forceSelectionUpdate = YES; +} + +- (IBAction) setBranchFilter:(id)sender +{ + repository.currentBranchFilter = [(NSView*)sender tag]; + [PBGitDefaults setBranchFilter:repository.currentBranchFilter]; + [self updateBranchFilterMatrix]; + forceSelectionUpdate = YES; +} + +- (void)keyDown:(NSEvent*)event +{ + if ([[event charactersIgnoringModifiers] isEqualToString: @"f"] && [event modifierFlags] & NSEventModifierFlagOption && [event modifierFlags] & NSCommandKeyMask) + [superController.window makeFirstResponder: searchField]; + else + [super keyDown: event]; +} + +- (void)setHistorySearch:(NSString *)searchString mode:(PBHistorySearchMode)mode +{ + [searchController setHistorySearch:searchString mode:mode]; +} + +// NSSearchField (actually textfields in general) prevent the normal Find operations from working. Setup custom actions for the +// next and previous menuitems (in MainMenu.nib) so they will work when the search field is active. When searching for text in +// a file make sure to call the Find panel's action method instead. +- (IBAction)selectNext:(id)sender +{ + NSResponder *firstResponder = [[[self view] window] firstResponder]; + if ([firstResponder isKindOfClass:[PBQLTextView class]]) { + [(PBQLTextView *)firstResponder performFindPanelAction:sender]; + return; + } + + [searchController selectNextResult]; +} +- (IBAction)selectPrevious:(id)sender +{ + NSResponder *firstResponder = [[[self view] window] firstResponder]; + if ([firstResponder isKindOfClass:[PBQLTextView class]]) { + [(PBQLTextView *)firstResponder performFindPanelAction:sender]; + return; + } + + [searchController selectPreviousResult]; +} + +- (IBAction) copy:(id)sender +{ + [GitXCommitCopier putStringToPasteboard:[GitXCommitCopier toSHAAndHeadingString:commitController.selectedObjects]]; +} + +- (IBAction) copySHA:(id)sender +{ + [GitXCommitCopier putStringToPasteboard:[GitXCommitCopier toFullSHA:commitController.selectedObjects]]; +} + +- (IBAction) copyShortName:(id)sender +{ + [GitXCommitCopier putStringToPasteboard:[GitXCommitCopier toShortName:commitController.selectedObjects]]; +} + +- (IBAction) copyPatch:(id)sender +{ + [GitXCommitCopier putStringToPasteboard:[GitXCommitCopier toPatch:commitController.selectedObjects]]; +} + +- (IBAction) toggleQLPreviewPanel:(id)sender +{ + if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) + [[QLPreviewPanel sharedPreviewPanel] orderOut:nil]; + else + [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil]; +} + +- (IBAction) refresh:(id)sender +{ + [repository forceUpdateRevisions]; +} + +- (void) updateView +{ + [self updateKeys]; +} + +- (NSResponder *)firstResponder; +{ + return commitList; +} + +- (void) scrollSelectionToTopOfViewFrom:(NSInteger)oldIndex +{ + if (oldIndex == NSNotFound) + oldIndex = 0; + + NSInteger newIndex = commitController.selectionIndexes.firstIndex; + + if (newIndex > oldIndex) { + CGFloat sviewHeight = commitList.superview.bounds.size.height; + CGFloat rowHeight = commitList.rowHeight; + NSInteger visibleRows = lround(sviewHeight / rowHeight); + newIndex += (visibleRows - 1); + if (newIndex >= [commitController.content count]) + newIndex = [commitController.content count] - 1; + } + + if (newIndex != oldIndex) { + commitList.useAdjustScroll = YES; + } + + [commitList scrollRowToVisible:newIndex]; + commitList.useAdjustScroll = NO; +} + +- (NSArray *) selectedObjectsForOID:(GTOID *)commitOID +{ + NSPredicate *selection = [NSPredicate predicateWithFormat:@"OID == %@", commitOID]; + NSArray *selectionCommits = [[commitController content] filteredArrayUsingPredicate:selection]; + + if ((selectionCommits.count == 0) && [self firstCommit] != nil) { + selectionCommits = @[[self firstCommit]]; + } + + return selectionCommits; +} + +- (void)selectCommit:(GTOID *)commitOID +{ + if (!forceSelectionUpdate && [[[commitController.selectedObjects lastObject] OID] isEqual:commitOID]) { + return; + } + + NSArray *selectedObjects = [self selectedObjectsForOID:commitOID]; + [commitController setSelectedObjects:selectedObjects]; + + NSInteger oldIndex = [[commitController selectionIndexes] firstIndex]; + [self scrollSelectionToTopOfViewFrom:oldIndex]; + + forceSelectionUpdate = NO; +} + +- (BOOL) hasNonlinearPath +{ + return [commitController filterPredicate] || [[commitController sortDescriptors] count] > 0; +} + +- (void)closeView +{ + if (commitController) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [commitController removeObserver:self forKeyPath:@"selection"]; + [commitController removeObserver:self forKeyPath:@"arrangedObjects.@count"]; + [treeController removeObserver:self forKeyPath:@"selection"]; + + [repository.revisionList removeObserver:self forKeyPath:@"isUpdating"]; + [repository removeObserver:self forKeyPath:@"currentBranch"]; + [repository removeObserver:self forKeyPath:@"refs"]; + [repository removeObserver:self forKeyPath:@"currentBranchFilter"]; + } + + [webHistoryController closeView]; + [fileView closeView]; + + [super closeView]; +} + +#pragma mark Table Column Methods +- (NSMenu *)tableColumnMenu +{ + NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + for (NSTableColumn *column in [commitList tableColumns]) { + NSMenuItem *item = [[NSMenuItem alloc] init]; + [item setTitle:[[column headerCell] stringValue]]; + [item bind:@"value" + toObject:column + withKeyPath:@"hidden" + options:[NSDictionary dictionaryWithObject:@"NSNegateBoolean" forKey:NSValueTransformerNameBindingOption]]; + [menu addItem:item]; + } + return menu; +} + +#pragma mark Tree Context Menu Methods + +- (void)showCommitsFromTree:(id)sender +{ + NSString *searchString = [(NSArray *)[sender representedObject] componentsJoinedByString:@" "]; + [self setHistorySearch:searchString mode:PBHistorySearchModePath]; +} + +- (void) checkoutFiles:(id)sender +{ + NSMutableArray *files = [NSMutableArray array]; + for (NSString *filePath in [sender representedObject]) + [files addObject:[filePath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; + + NSError *error = nil; + BOOL success = [repository checkoutFiles:files fromRefish:self.selectedCommits.firstObject error:&error]; + if (!success) { + [self.windowController showErrorSheet:error]; + } + +} + +- (void) diffFilesAction:(id)sender +{ + /* TODO: Move that to the document */ + [PBDiffWindowController showDiffWindowWithFiles:[sender representedObject] fromCommit:self.selectedCommits.firstObject diffCommit:nil]; +} + +- (NSMenu *)contextMenuForTreeView +{ + NSArray *filePaths = [[treeController selectedObjects] valueForKey:@"fullPath"]; + + NSMenu *menu = [[NSMenu alloc] init]; + for (NSMenuItem *item in [self menuItemsForPaths:filePaths]) + [menu addItem:item]; + return menu; +} + +- (NSArray *)menuItemsForPaths:(NSArray *)paths +{ + NSMutableArray *filePaths = [NSMutableArray array]; + for (NSString *filePath in paths) + [filePaths addObject:[filePath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; + + BOOL multiple = [filePaths count] != 1; + NSString *historyItemTitle = multiple + ? NSLocalizedString(@"Show history of files", @"Show history menu item for multiple files") + : NSLocalizedString(@"Show history of file", @"Show history menu item for single file"); + NSMenuItem *historyItem = [[NSMenuItem alloc] initWithTitle:historyItemTitle + action:@selector(showCommitsFromTree:) + keyEquivalent:@""]; + + PBGitRef *headRef = [[repository headRef] ref]; + NSString *headRefName = [headRef shortName]; + NSString *diffTitleFormat = multiple + ? NSLocalizedString(@"Diff files with %@", @"Diff with ref menu item for multiple files") + : NSLocalizedString(@"Diff file with %@", @"Diff with ref menu item for single file"); + NSString *diffTitle = [NSString stringWithFormat:diffTitleFormat, headRefName]; + BOOL isHead = [self.selectedCommits.firstObject.OID isEqual:repository.headOID]; + NSMenuItem *diffItem = [[NSMenuItem alloc] initWithTitle:diffTitle + action:isHead ? nil : @selector(diffFilesAction:) + keyEquivalent:@""]; + + NSString *checkoutItemTitle = multiple + ? NSLocalizedString(@"Checkout files", @"Checkout menu item for multiple files") + : NSLocalizedString(@"Checkout file", @"Checkout menu item for single file"); + NSMenuItem *checkoutItem = [[NSMenuItem alloc] initWithTitle:checkoutItemTitle + action:@selector(checkoutFiles:) + keyEquivalent:@""]; + + NSString *finderItemTitle = NSLocalizedString(@"Reveal in Finder", @"Show in Finder menu item"); + NSMenuItem *finderItem = [[NSMenuItem alloc] initWithTitle:finderItemTitle + action:@selector(revealInFinder:) + keyEquivalent:@""]; + + NSString *openFilesItemTitle = multiple + ? NSLocalizedString(@"Open Files", @"Open menu item for multiple files") + : NSLocalizedString(@"Open File", @"Open menu item for single file"); + NSMenuItem *openFilesItem = [[NSMenuItem alloc] initWithTitle:openFilesItemTitle + action:@selector(openFiles:) + keyEquivalent:@""]; + + NSArray *menuItems = [NSArray arrayWithObjects:historyItem, diffItem, checkoutItem, finderItem, openFilesItem, nil]; + for (NSMenuItem *item in menuItems) { + [item setRepresentedObject:filePaths]; + } + + return menuItems; +} + +#pragma mark - +#pragma mark Quick Look + +#pragma mark + +- (NSInteger)numberOfPreviewItemsInPreviewPanel:(id)panel +{ + return [[fileBrowser selectedRowIndexes] count]; +} + +- (id )previewPanel:(id)panel previewItemAtIndex:(NSInteger)index +{ + PBGitTree *treeItem = (PBGitTree *)[[treeController selectedObjects] objectAtIndex:index]; + NSURL *previewURL = [NSURL fileURLWithPath:[treeItem tmpFileNameForContents]]; + + return (id )previewURL; +} + +#pragma mark + +- (BOOL)previewPanel:(id)panel handleEvent:(NSEvent *)event +{ + // redirect all key down events to the table view + if ([event type] == NSEventTypeKeyDown) { + [fileBrowser keyDown:event]; + return YES; + } + return NO; +} + +// This delegate method provides the rect on screen from which the panel will zoom. +- (NSRect)previewPanel:(id)panel sourceFrameOnScreenForPreviewItem:(id )item +{ + NSInteger index = [fileBrowser rowForItem:[[treeController selectedNodes] objectAtIndex:0]]; + if (index == NSNotFound) { + return NSZeroRect; + } + + NSRect iconRect = [fileBrowser frameOfCellAtColumn:0 row:index]; + + // check that the icon rect is visible on screen + NSRect visibleRect = [fileBrowser visibleRect]; + + if (!NSIntersectsRect(visibleRect, iconRect)) { + return NSZeroRect; + } + + // convert icon rect to screen coordinates + iconRect = [fileBrowser.window.contentView convertRect:iconRect fromView:fileBrowser]; + iconRect = [fileBrowser.window convertRectToScreen:iconRect]; + + return iconRect; +} + +@end diff --git a/Classes/Controllers/PBGitRepositoryDocument.h b/Classes/Controllers/PBGitRepositoryDocument.h new file mode 100644 index 000000000..9eb9d55cd --- /dev/null +++ b/Classes/Controllers/PBGitRepositoryDocument.h @@ -0,0 +1,34 @@ +// +// PBGitRepositoryDocument.h +// GitX +// +// Created by Etienne on 31/07/2014. +// +// + +#import + +@class PBGitRepository; +@class PBGitRevSpecifier; +@class PBGitWindowController; + +extern NSString *PBGitRepositoryDocumentType; + +@interface PBGitRepositoryDocument : NSDocument + +@property (nonatomic, strong, readonly) PBGitRepository *repository; + + +// Scripting Bridge +- (void)findInModeScriptCommand:(NSScriptCommand *)command; + +- (IBAction)showCommitView:(id)sender; +- (IBAction)showHistoryView:(id)sender; + +- (void)selectRevisionSpecifier:(PBGitRevSpecifier *)specifier; + +- (PBGitWindowController *)windowController; + +- (void)handleGitXScriptingArguments:(NSArray *)arguments; + +@end diff --git a/Classes/Controllers/PBGitRepositoryDocument.m b/Classes/Controllers/PBGitRepositoryDocument.m new file mode 100644 index 000000000..54a690066 --- /dev/null +++ b/Classes/Controllers/PBGitRepositoryDocument.m @@ -0,0 +1,223 @@ +// +// PBGitRepositoryDocument.m +// GitX +// +// Created by Etienne on 31/07/2014. +// +// + +#import "PBGitRepositoryDocument.h" +#import "PBGitRepository.h" +#import "PBGitWindowController.h" +#import "PBGitRevSpecifier.h" +#import "PBGitBinary.h" +#import "GitXScriptingConstants.h" +#import "PBRepositoryFinder.h" +#import "PBGitDefaults.h" +#import "PBOpenShallowRepositoryErrorRecoveryAttempter.h" +#import "PBError.h" + +NSString *PBGitRepositoryDocumentType = @"Git Repository"; + +@implementation PBGitRepositoryDocument + +- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError +{ + if (![PBGitBinary path]) + { + return PBReturnError(outError, @"Unable to find git", [PBGitBinary notFoundError], nil); + } + + BOOL isDirectory = FALSE; + [[NSFileManager defaultManager] fileExistsAtPath:[absoluteURL path] isDirectory:&isDirectory]; + if (!isDirectory) { + return PBReturnError(outError, @"Unable to read files", @"Reading files is not supported", nil); + } + + _repository = [[PBGitRepository alloc] initWithURL:absoluteURL error:outError]; + if (!_repository) { + return NO; + } + if (_repository.isShallowRepository) { + if (outError) { + NSDictionary* userInfo = @{ + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString( + @"The repository is shallowly cloned, which is not supported by GitX. Please run “git fetch --unshallow” on the repository before opening it with GitX.", + @"Recovery suggestion when opening a shallow repository"), + NSLocalizedRecoveryOptionsErrorKey: [PBOpenShallowRepositoryErrorRecoveryAttempter errorDialogButtonNames], + NSRecoveryAttempterErrorKey: [[PBOpenShallowRepositoryErrorRecoveryAttempter alloc] initWithURL:_repository.workingDirectoryURL] + }; + *outError = [NSError errorWithDomain:PBGitXErrorDomain code:0 userInfo:userInfo]; + } + return NO; + } + + + return YES; +} + +- (void)close +{ + /* FIXME: Check that this deallocs the repo */ +// [revisionList cleanup]; + + [super close]; +} + +- (BOOL)isDocumentEdited +{ + return NO; +} + +- (NSString *)displayName +{ + // Build our display name depending on the current HEAD and whether it's detached or not + if (self.repository.gtRepo.isHEADDetached) + return [NSString stringWithFormat:NSLocalizedString(@"%@ (detached HEAD)", @""), self.repository.projectName]; + + if (self.repository.gtRepo.isHEADUnborn) + return [NSString stringWithFormat:NSLocalizedString(@"%@ (unborn HEAD)", @""), self.repository.projectName]; + + return [NSString stringWithFormat:NSLocalizedString(@"%@ (branch: %@)", @""), self.repository.projectName, self.repository.headRef.description]; +} + +- (void)makeWindowControllers +{ + // Create our custom window controller +#ifndef CLI + [self addWindowController:[[PBGitWindowController alloc] init]]; +#endif +} + +- (PBGitWindowController *)windowController +{ + if ([[self windowControllers] count] == 0) + return NULL; + + return [[self windowControllers] objectAtIndex:0]; +} + +- (IBAction)showCommitView:(id)sender { + [[self windowController] showCommitView:sender]; +} + +- (IBAction)showHistoryView:(id)sender { + [[self windowController] showHistoryView:sender]; +} + +- (void)selectRevisionSpecifier:(PBGitRevSpecifier *)specifier { + PBGitRevSpecifier *spec = [self.repository addBranch:specifier]; + self.repository.currentBranch = spec; + [self showHistoryView:self]; +} + +- (void)showWindows +{ + NSScriptCommand *command = [NSScriptCommand currentCommand]; + + if (command) { + // Check if `gitx` has provided some arguments + NSURL *repoURL = [command directParameter]; + + // on app launch there may be many repositories opening, so double check that this is the right repo + if (repoURL && [repoURL isKindOfClass:[NSURL class]]) { + repoURL = [PBRepositoryFinder gitDirForURL:repoURL]; + if ([repoURL isEqual:_repository.gitURL]) { + NSArray *arguments = command.arguments[@"openOptions"]; + [self handleGitXScriptingArguments:arguments]; + } + } + } + + [[[self windowController] window] setTitle:[self displayName]]; + [super showWindows]; +} + +#pragma mark - +#pragma mark AppleScript support + +- (void)handleRevListArguments:(NSArray *)arguments +{ + if (![arguments count]) + return; + + PBGitRevSpecifier *revListSpecifier = nil; + + // the argument may be a branch or tag name but will probably not be the full reference + if ([arguments count] == 1) { + PBGitRef *refArgument = [self.repository refForName:[arguments lastObject]]; + if (refArgument) { + revListSpecifier = [[PBGitRevSpecifier alloc] initWithRef:refArgument]; + revListSpecifier.workingDirectory = self.repository.workingDirectoryURL; + } + } + + if (!revListSpecifier) { + revListSpecifier = [[PBGitRevSpecifier alloc] initWithParameters:arguments]; + revListSpecifier.workingDirectory = self.repository.workingDirectoryURL; + } + + self.repository.currentBranch = [self.repository addBranch:revListSpecifier]; + [PBGitDefaults setShowStageView:NO]; + [self.windowController showHistoryView:self]; +} + +- (void)handleBranchFilterEventForFilter:(PBGitXBranchFilterType)filter additionalArguments:(NSArray *)arguments +{ + self.repository.currentBranchFilter = filter; + [PBGitDefaults setShowStageView:NO]; + [self.windowController showHistoryView:self]; + + // treat any additional arguments as a rev-list specifier + if ([arguments count] > 1) { + arguments = [arguments subarrayWithRange:NSMakeRange(1, arguments.count)]; + [self handleRevListArguments:arguments]; + } +} + +- (void)handleGitXScriptingArguments:(NSArray *)arguments +{ + if (![arguments count]) + return; + + NSString *firstArgument = [arguments objectAtIndex:0]; + + if ([firstArgument isEqualToString:@"-c"] || [firstArgument isEqualToString:@"--commit"]) { + [PBGitDefaults setShowStageView:YES]; + [self.windowController showCommitView:self]; + return; + } + + if ([firstArgument isEqualToString:@"--all"]) { + [self handleBranchFilterEventForFilter:kGitXAllBranchesFilter additionalArguments:arguments]; + return; + } + + if ([firstArgument isEqualToString:@"--local"]) { + [self handleBranchFilterEventForFilter:kGitXLocalRemoteBranchesFilter additionalArguments:arguments]; + return; + } + + if ([firstArgument isEqualToString:@"--branch"]) { + [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]; +} + +// for the scripting bridge +- (void)findInModeScriptCommand:(NSScriptCommand *)command +{ + NSDictionary *arguments = [command arguments]; + NSString *searchString = [arguments objectForKey:kGitXFindSearchStringKey]; + if (searchString) { + [PBGitDefaults setShowStageView:NO]; + [self.windowController showHistoryView:self]; + PBHistorySearchMode mode = PBSearchModeForInteger([[arguments objectForKey:kGitXFindInModeKey] integerValue]); + [self.windowController setHistorySearch:searchString mode:mode]; + } +} + +@end diff --git a/Classes/Controllers/PBGitSidebarController.h b/Classes/Controllers/PBGitSidebarController.h new file mode 100644 index 000000000..11ef7e613 --- /dev/null +++ b/Classes/Controllers/PBGitSidebarController.h @@ -0,0 +1,45 @@ +// +// PBGitSidebar.h +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBViewController.h" +#import "PBHistorySearchMode.h" + +@class PBSourceViewItem; +@class PBGitHistoryController; +@class PBGitCommitController; + +@interface PBGitSidebarController : PBViewController { + __weak IBOutlet NSWindow *window; + __weak IBOutlet NSOutlineView *sourceView; + __weak IBOutlet NSView *sourceListControlsView; + __weak IBOutlet NSPopUpButton *actionButton; + __weak IBOutlet NSSegmentedControl *remoteControls; + + NSMutableArray *items; + + /* Specific things */ + PBSourceViewItem *stage; + + PBSourceViewItem *branches, *remotes, *tags, *others, *submodules, *stashes; +} + +- (void) selectStage; +- (void) selectCurrentBranch; + +- (NSMenu *) menuForRow:(NSInteger)row; +- (void) menuNeedsUpdate:(NSMenu *)menu; + +- (IBAction) fetchPullPushAction:(id)sender; + +@property(readonly) NSMutableArray *items; +@property(readonly) PBSourceViewItem *remotes; +@property(readonly) NSOutlineView *sourceView; +@property(readonly) NSView *sourceListControlsView; + +@end diff --git a/Classes/Controllers/PBGitSidebarController.m b/Classes/Controllers/PBGitSidebarController.m new file mode 100644 index 000000000..088295ad5 --- /dev/null +++ b/Classes/Controllers/PBGitSidebarController.m @@ -0,0 +1,507 @@ +// +// PBGitSidebar.m +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "PBGitSidebarController.h" +#import "PBSourceViewItems.h" +#import "PBGitHistoryController.h" +#import "PBGitCommitController.h" +#import "PBRefController.h" +#import "PBSourceViewCell.h" +#import "NSOutlineViewExt.h" +#import "PBAddRemoteSheet.h" +#import "PBGitDefaults.h" +#import "PBHistorySearchController.h" +#import "PBGitStash.h" +#import "PBGitSVStashItem.h" +#import "PBGitRef.h" + +@interface PBGitSidebarController () + +- (void)populateList; +- (PBSourceViewItem *)addRevSpec:(PBGitRevSpecifier *)revSpec; +- (PBSourceViewItem *)itemForRev:(PBGitRevSpecifier *)rev; +- (void) removeRevSpec:(PBGitRevSpecifier *)rev; +- (void) updateActionMenu; +- (void) updateRemoteControls; +@end + +@implementation PBGitSidebarController +@synthesize items; +@synthesize remotes; +@synthesize sourceView; +@synthesize sourceListControlsView; + +- (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller +{ + self = [super initWithRepository:theRepository superController:controller]; + [sourceView setDelegate:self]; + items = [NSMutableArray array]; + + return self; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + window.contentView = self.view; + [self populateList]; + + [repository addObserver:self forKeyPath:@"currentBranch" options:0 context:@"currentBranchChange"]; + [repository addObserver:self forKeyPath:@"branches" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:@"branchesModified"]; + [repository addObserver:self forKeyPath:@"stashes" options:0 context:@"stashesModified"]; + + [sourceView setTarget:self]; + [sourceView setDoubleAction:@selector(doubleClicked:)]; + + [self menuNeedsUpdate:[actionButton menu]]; + + if ([PBGitDefaults showStageView]) + [self selectStage]; + else + [self selectCurrentBranch]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(expandCollapseItem:) name:NSOutlineViewItemWillExpandNotification object:sourceView]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(expandCollapseItem:) name:NSOutlineViewItemWillCollapseNotification object:sourceView]; + +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSOutlineViewItemWillExpandNotification object:sourceView]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSOutlineViewItemWillCollapseNotification object:sourceView]; +} + +- (void)closeView +{ + [repository removeObserver:self forKeyPath:@"currentBranch"]; + [repository removeObserver:self forKeyPath:@"branches"]; + [repository removeObserver:self forKeyPath:@"stashes"]; + + [super closeView]; +} + +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([@"currentBranchChange" isEqualToString:(__bridge NSString*)context]) { + [sourceView reloadData]; + [self selectCurrentBranch]; + return; + } + + if ([@"branchesModified" isEqualToString:(__bridge NSString*)context]) { + NSInteger changeKind = [(NSNumber *)[change objectForKey:NSKeyValueChangeKindKey] intValue]; + + if (changeKind == NSKeyValueChangeInsertion) { + NSArray *newRevSpecs = [change objectForKey:NSKeyValueChangeNewKey]; + for (PBGitRevSpecifier *rev in newRevSpecs) { + PBSourceViewItem *item = [self addRevSpec:rev]; + [sourceView PBExpandItem:item expandParents:YES]; + } + } + else if (changeKind == NSKeyValueChangeRemoval) { + NSArray *removedRevSpecs = [change objectForKey:NSKeyValueChangeOldKey]; + for (PBGitRevSpecifier *rev in removedRevSpecs) + [self removeRevSpec:rev]; + } + return; + } + + if ([@"stashesModified" isEqualToString:(__bridge NSString*)context]) { + + for (PBGitSVStashItem *stashItem in stashes.sortedChildren) + [stashes removeChild:stashItem]; + + for (PBGitStash *stash in repository.stashes) + [stashes addChild: [PBGitSVStashItem itemWithStash:stash]]; + + [sourceView expandItem:stashes]; + [sourceView reloadItem:stashes reloadChildren:YES]; + + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (PBSourceViewItem *) selectedItem +{ + NSInteger index = [sourceView selectedRow]; + PBSourceViewItem *item = [sourceView itemAtRow:index]; + + return item; +} + +- (void) selectStage +{ + NSIndexSet *index = [NSIndexSet indexSetWithIndex:[sourceView rowForItem:stage]]; + [sourceView selectRowIndexes:index byExtendingSelection:NO]; +} + +- (void) selectCurrentBranch +{ + PBGitRevSpecifier *rev = repository.currentBranch; + if (!rev) { + [repository reloadRefs]; + [repository readCurrentBranch]; + return; + } + + PBSourceViewItem *item = [self addRevSpec:rev]; + if (item) { + [sourceView reloadData]; + + [sourceView PBExpandItem:item expandParents:YES]; + NSIndexSet *index = [NSIndexSet indexSetWithIndex:[sourceView rowForItem:item]]; + + [sourceView selectRowIndexes:index byExtendingSelection:NO]; + } +} + +- (PBSourceViewItem *) itemForRev:(PBGitRevSpecifier *)rev +{ + PBSourceViewItem *foundItem = nil; + for (PBSourceViewItem *item in items) + if ( (foundItem = [item findRev:rev]) != nil ) + return foundItem; + return nil; +} + +- (PBSourceViewItem *)addRevSpec:(PBGitRevSpecifier *)rev +{ + PBSourceViewItem *item = nil; + for (PBSourceViewItem *it in items) + if ( (item = [it findRev:rev]) != nil ) + return item; + + if (![rev isSimpleRef]) { + [others addChild:[PBSourceViewItem itemWithRevSpec:rev]]; + return item; + } + + NSArray *pathComponents = [[rev simpleRef] componentsSeparatedByString:@"/"]; + if ([pathComponents count] < 2) + [branches addChild:[PBSourceViewItem itemWithRevSpec:rev]]; + else if ([[pathComponents objectAtIndex:1] isEqualToString:@"heads"]) + [branches addRev:rev toPath:[pathComponents subarrayWithRange:NSMakeRange(2, [pathComponents count] - 2)]]; + else if ([[rev simpleRef] hasPrefix:@"refs/tags/"]) + [tags addRev:rev toPath:[pathComponents subarrayWithRange:NSMakeRange(2, [pathComponents count] - 2)]]; + else if ([[rev simpleRef] hasPrefix:@"refs/remotes/"]) + [remotes addRev:rev toPath:[pathComponents subarrayWithRange:NSMakeRange(2, [pathComponents count] - 2)]]; + return item; +} + +- (void) removeRevSpec:(PBGitRevSpecifier *)rev +{ + PBSourceViewItem *item = [self itemForRev:rev]; + + if (!item) + return; + + PBSourceViewItem *parent = item.parent; + [parent removeChild:item]; + [sourceView reloadData]; +} + +- (void) openSubmoduleFromMenuItem:(NSMenuItem *)menuItem +{ + [self openSubmoduleAtURL:[menuItem representedObject]]; +} + +- (void) openSubmoduleAtURL:(NSURL *)submoduleURL +{ + [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:submoduleURL display:YES completionHandler:^(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error) { + if (error) { + [self.windowController showErrorSheet:error]; + } + }]; +} + +#pragma mark NSOutlineView delegate methods + +- (void)outlineViewSelectionDidChange:(NSNotification *)notification +{ + NSInteger index = [sourceView selectedRow]; + PBSourceViewItem *item = [sourceView itemAtRow:index]; + + if ([item revSpecifier]) { + if (![repository.currentBranch isEqual:[item revSpecifier]]) + repository.currentBranch = [item revSpecifier]; + [superController changeContentController:superController.historyViewController]; + [PBGitDefaults setShowStageView:NO]; + } + + if (item == stage) { + [superController changeContentController:superController.commitViewController]; + [PBGitDefaults setShowStageView:YES]; + } + + [self updateActionMenu]; + [self updateRemoteControls]; +} + +- (void)doubleClicked:(id)object { + NSInteger rowNumber = [sourceView selectedRow]; + + id item = [sourceView itemAtRow:rowNumber]; + if ([item isKindOfClass:[PBGitSVSubmoduleItem class]]) { + PBGitSVSubmoduleItem *subModule = item; + + [self openSubmoduleAtURL:[subModule path]]; + } else if ([item isKindOfClass:[PBGitSVBranchItem class]]) { + PBGitSVBranchItem *branch = item; + + NSError *error = nil; + BOOL success = [repository checkoutRefish:[branch ref] error:&error]; + if (!success) { + [self.windowController showErrorSheet:error]; + } + } +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if ([item isKindOfClass:[PBGitSVSubmoduleItem class]]) { + NSLog(@"hi"); + } + return NO; +} +#pragma mark NSOutlineView delegate methods +- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item +{ + return [item isGroupItem]; +} + +- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(PBSourceViewCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(PBSourceViewItem *)item +{ + cell.isCheckedOut = [item.revSpecifier isEqual:[repository headRef]]; + [cell setImage:[item icon]]; +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item +{ + return ![item isGroupItem]; +} + +// +// The next method is necessary to hide the triangle for uncollapsible items +// That is, items which should always be displayed, such as the Project group. +// This also moves the group item to the left edge. +- (BOOL) outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item +{ + return ![item isUncollapsible]; +} + +- (void)populateList +{ + PBSourceViewItem *project = [PBSourceViewItem groupItemWithTitle:[repository projectName]]; + project.isUncollapsible = YES; + + stage = [PBGitSVStageItem stageItem]; + [project addChild:stage]; + + branches = [PBSourceViewItem groupItemWithTitle:@"Branches"]; + remotes = [PBSourceViewItem groupItemWithTitle:@"Remotes"]; + tags = [PBSourceViewItem groupItemWithTitle:@"Tags"]; + stashes = [PBSourceViewItem groupItemWithTitle:@"Stashes"]; + submodules = [PBSourceViewItem groupItemWithTitle:@"Submodules"]; + others = [PBSourceViewItem groupItemWithTitle:@"Other"]; + + for (PBGitStash *stash in repository.stashes) + [stashes addChild: [PBGitSVStashItem itemWithStash:stash]]; + + for (PBGitRevSpecifier *rev in repository.branches) { + [self addRevSpec:rev]; + } + + for (GTSubmodule *sub in repository.submodules) { + [submodules addChild: [PBGitSVSubmoduleItem itemWithSubmodule:sub]]; + } + + [items addObject:project]; + [items addObject:branches]; + [items addObject:remotes]; + [items addObject:tags]; + [items addObject:stashes]; + [items addObject:submodules]; + [items addObject:others]; + + [sourceView reloadData]; + [sourceView expandItem:project]; + [sourceView expandItem:branches expandChildren:YES]; + [sourceView expandItem:remotes]; + [sourceView expandItem:stashes]; + [sourceView expandItem:submodules]; + + [sourceView reloadItem:nil reloadChildren:YES]; +} + +- (void)expandCollapseItem:(NSNotification*)aNotification +{ + NSObject* child = [[aNotification userInfo] valueForKey:@"NSObject"]; + if ([child isKindOfClass:[PBSourceViewItem class]]) { + ((PBSourceViewItem*)child).isExpanded = [aNotification.name isEqualToString:NSOutlineViewItemWillExpandNotification]; + } +} + +#pragma mark NSOutlineView Datasource methods + +- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item +{ + if (!item) + return [items objectAtIndex:index]; + + return [[(PBSourceViewItem *)item sortedChildren] objectAtIndex:index]; +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item +{ + return [[(PBSourceViewItem *)item sortedChildren] count] > 0; +} + +- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item +{ + if (!item) + return [items count]; + + return [[(PBSourceViewItem *)item sortedChildren] count]; +} + +- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +{ + return [(PBSourceViewItem *)item title]; +} + + +#pragma mark Menus + +- (void) updateActionMenu +{ + [actionButton setEnabled:([[self selectedItem] ref] != nil || [[self selectedItem] isKindOfClass:[PBGitSVSubmoduleItem class]])]; +} + +- (void) addMenuItemsForRef:(PBGitRef *)ref toMenu:(NSMenu *)menu +{ + if (!ref) + return; + + for (NSMenuItem *menuItem in [superController.historyViewController.refController menuItemsForRef:ref]) + [menu addItem:menuItem]; +} + +- (void) addMenuItemsForSubmodule:(PBGitSVSubmoduleItem *)submodule toMenu:(NSMenu *)menu +{ + if (!submodule) + return; + + NSMenuItem *menuItem = [menu addItemWithTitle:NSLocalizedString(@"Open Submodule", @"Open Submodule menu item") action:@selector(openSubmoduleFromMenuItem:) keyEquivalent:@""]; + + [menuItem setTarget:self]; + [menuItem setRepresentedObject:[submodule path]]; +} + +- (NSMenuItem *) actionIconItem +{ + NSMenuItem *actionIconItem = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""]; + NSImage *actionIcon = [NSImage imageNamed:@"NSActionTemplate"]; + [actionIcon setSize:NSMakeSize(12, 12)]; + [actionIconItem setImage:actionIcon]; + + return actionIconItem; +} + +- (NSMenu *) menuForRow:(NSInteger)row +{ + PBSourceViewItem *viewItem = [sourceView itemAtRow:row]; + PBGitRef *ref = [viewItem ref]; + NSMenu *menu = [[NSMenu alloc] init]; + + [menu setAutoenablesItems:NO]; + + if (ref) { + [self addMenuItemsForRef:ref toMenu:menu]; + } + + if ([viewItem isKindOfClass:[PBGitSVSubmoduleItem class]]) { + [self addMenuItemsForSubmodule:(PBGitSVSubmoduleItem *)viewItem toMenu:menu]; + } + + return menu; +} + +// delegate of the action menu +- (void) menuNeedsUpdate:(NSMenu *)menu +{ + [actionButton removeAllItems]; + [menu addItem:[self actionIconItem]]; + + PBGitRef *ref = [[self selectedItem] ref]; + [self addMenuItemsForRef:ref toMenu:menu]; + + if ([[self selectedItem] isKindOfClass:[PBGitSVSubmoduleItem class]]) { + [self addMenuItemsForSubmodule:(PBGitSVSubmoduleItem *)[self selectedItem] toMenu:menu]; + } +} + + +#pragma mark Remote controls + +enum { + kAddRemoteSegment = 0, + kFetchSegment = 1, + kPullSegment = 2, + kPushSegment = 3 +}; + +- (void) updateRemoteControls +{ + BOOL hasRemote = NO; + + PBGitRef *ref = [[self selectedItem] ref]; + if ([ref isRemote] || ([ref isBranch] && [[repository remoteRefForBranch:ref error:NULL] remoteName])) + hasRemote = YES; + + [remoteControls setEnabled:hasRemote forSegment:kFetchSegment]; + [remoteControls setEnabled:hasRemote forSegment:kPullSegment]; + [remoteControls setEnabled:hasRemote forSegment:kPushSegment]; +} + +- (IBAction) fetchPullPushAction:(id)sender +{ + NSInteger selectedSegment = [sender selectedSegment]; + + if (selectedSegment == kAddRemoteSegment) { + [self tryToPerform:@selector(addRemote:) with:self]; + return; + } + + NSInteger index = [sourceView selectedRow]; + PBSourceViewItem *item = [sourceView itemAtRow:index]; + PBGitRef *ref = [[item revSpecifier] ref]; + + if (!ref && (item.parent == remotes)) + ref = [PBGitRef refFromString:[kGitXRemoteRefPrefix stringByAppendingString:[item title]]]; + + if (![ref isRemote] && ![ref isBranch]) + return; + + PBGitRef *remoteRef = [repository remoteRefForBranch:ref error:NULL]; + if (!remoteRef) + return; + + if (selectedSegment == kFetchSegment) { + [self.windowController performFetchForRef:ref]; + } else if (selectedSegment == kPullSegment) { + [self.windowController performPullForBranch:ref remote:remoteRef rebase:NO]; + } else if (selectedSegment == kPushSegment && ref.isRemote) { + [self.windowController performPushForBranch:nil toRemote:remoteRef]; + } else if (selectedSegment == kPushSegment && ref.isBranch) { + [self.windowController performPushForBranch:ref toRemote:remoteRef]; + } +} + +@end diff --git a/Classes/Controllers/PBGitWindowController.h b/Classes/Controllers/PBGitWindowController.h new file mode 100644 index 000000000..8b8cd94c8 --- /dev/null +++ b/Classes/Controllers/PBGitWindowController.h @@ -0,0 +1,83 @@ +// +// PBDetailController.h +// GitX +// +// Created by Pieter de Bie on 16-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBHistorySearchMode.h" + +@class PBViewController; +@class PBGitSidebarController; +@class PBGitCommitController; +@class PBGitHistoryController; +@class PBGitRepository; +@class RJModalRepoSheet; +@class PBGitRef; +@class PBGitRepositoryDocument; + +@interface PBGitWindowController : NSWindowController { + __weak PBViewController *contentController; + + PBGitSidebarController *sidebarController; + PBGitHistoryController *_historyViewController; + PBGitCommitController *_commitViewController; + + __weak IBOutlet NSView *sourceListControlsView; + __weak IBOutlet NSSplitView *splitView; + __weak IBOutlet NSView *sourceSplitView; + __weak IBOutlet NSView *contentSplitView; + __weak IBOutlet NSSegmentedControl *segmentedControl; + + __weak IBOutlet NSTextField *statusField; + __weak IBOutlet NSProgressIndicator *progressIndicator; +} + +@property (nonatomic, strong) PBGitRepository *repository; +/* This is assign because that's what NSWindowController says :-S */ +@property (assign) PBGitRepositoryDocument *document; +@property (readonly) PBGitHistoryController *historyViewController; +@property (readonly) PBGitCommitController *commitViewController; + +- (instancetype)init; + +- (void)changeContentController:(PBViewController *)controller; + +- (void)showCommitHookFailedSheet:(NSString *)messageText infoText:(NSString *)infoText commitController:(PBGitCommitController *)controller; + +- (void)showMessageSheet:(NSString *)messageText infoText:(NSString *)infoText; +- (void)showErrorSheet:(NSError *)error; + + +- (void)openURLs:(NSArray *)fileURLs; +- (void)revealURLsInFinder:(NSArray *)fileURLs; + +- (IBAction) showCommitView:(id)sender; +- (IBAction) showHistoryView:(id)sender; +- (IBAction) revealInFinder:(id)sender; +- (IBAction) openInTerminal:(id)sender; +- (IBAction) refresh:(id)sender; + +- (IBAction) showAddRemoteSheet:(id)sender; + +- (IBAction) fetchRemote:(id)sender; +- (IBAction) fetchAllRemotes:(id)sender; + +- (IBAction) pullRemote:(id)sender; +- (IBAction) pullRebaseRemote:(id)sender; +- (IBAction) pullDefaultRemote:(id)sender; +- (IBAction) pullRebaseDefaultRemote:(id)sender; + +- (IBAction) stashSave:(id) sender; +- (IBAction) stashSaveWithKeepIndex:(id) sender; +- (IBAction) stashPop:(id) sender; + +- (void)setHistorySearch:(NSString *)searchString mode:(PBHistorySearchMode)mode; + +- (void)performFetchForRef:(PBGitRef *)ref; +- (void)performPullForBranch:(PBGitRef *)branchRef remote:(PBGitRef *)remoteRef rebase:(BOOL)rebase; +- (void)performPushForBranch:(PBGitRef *)branchRef toRemote:(PBGitRef *)remoteRef; + +@end diff --git a/Classes/Controllers/PBGitWindowController.m b/Classes/Controllers/PBGitWindowController.m new file mode 100644 index 000000000..1afa885e5 --- /dev/null +++ b/Classes/Controllers/PBGitWindowController.m @@ -0,0 +1,882 @@ +// +// PBDetailController.m +// GitX +// +// Created by Pieter de Bie on 16-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitWindowController.h" +#import "PBGitHistoryController.h" +#import "PBGitCommitController.h" +#import "PBTerminalUtil.h" +#import "PBCommitHookFailedSheet.h" +#import "PBGitXMessageSheet.h" +#import "PBGitSidebarController.h" +#import "PBAddRemoteSheet.h" +#import "PBCreateBranchSheet.h" +#import "PBCreateTagSheet.h" +#import "PBGitDefaults.h" +#import "PBSourceViewItem.h" +#import "PBGitRevSpecifier.h" +#import "PBGitRef.h" +#import "PBError.h" +#import "PBRepositoryDocumentController.h" +#import "PBGitRepositoryDocument.h" +#import "PBRemoteProgressSheet.h" +#import "PBDiffWindowController.h" +#import "PBGitStash.h" +#import "PBGitCommit.h" + +@implementation PBGitWindowController + +@dynamic document; + +- (instancetype)init +{ + self = [super initWithWindowNibName:@"RepositoryWindow"]; + if (!self) + return nil; + + return self; +} + +- (PBGitRepository *)repository +{ + return [self.document repository]; +} + +- (void)synchronizeWindowTitleWithDocumentName +{ + [super synchronizeWindowTitleWithDocumentName]; + + if ([self isWindowLoaded]) { + // Point window proxy icon at project directory, not internal .git dir + [[self window] setRepresentedURL:self.repository.workingDirectoryURL]; + } +} + +- (void)windowWillClose:(NSNotification *)notification +{ +// NSLog(@"Window will close!"); + + if (sidebarController) + [sidebarController closeView]; + + [self.historyViewController closeView]; + [self.commitViewController closeView]; + + if (contentController) + [contentController removeObserver:self forKeyPath:@"status"]; +} + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + if ([menuItem action] == @selector(showCommitView:)) { + [menuItem setState:(contentController == _commitViewController) ? YES : NO]; + return ![self.repository isBareRepository]; + } else if ([menuItem action] == @selector(showHistoryView:)) { + [menuItem setState:(contentController != _commitViewController) ? YES : NO]; + return ![self.repository isBareRepository]; + } else if (menuItem.action == @selector(fetchRemote:)) { + return [self validateMenuItem:menuItem remoteTitle:@"Fetch “%@”" plainTitle:@"Fetch"]; + } else if (menuItem.action == @selector(pullRemote:)) { + return [self validateMenuItem:menuItem remoteTitle:@"Pull From “%@”" plainTitle:@"Pull"]; + } else if (menuItem.action == @selector(pullRebaseRemote:)) { + return [self validateMenuItem:menuItem remoteTitle:@"Pull From “%@” and Rebase" plainTitle:@"Pull and Rebase"]; + } + + return YES; +} + +- (BOOL) validateMenuItem:(NSMenuItem *)menuItem remoteTitle:(NSString *)localisationKeyWithRemote plainTitle:(NSString *)localizationKeyWithoutRemote +{ + PBGitRef *ref = [self selectedRef]; + if (!ref) + return NO; + + PBGitRef *remoteRef = [self.repository remoteRefForBranch:ref error:NULL]; + if (ref.isRemote || remoteRef) { + menuItem.title = [NSString stringWithFormat:NSLocalizedString(localisationKeyWithRemote, @""), (!remoteRef ? ref.remoteName : remoteRef.remoteName)]; + menuItem.representedObject = ref; + return YES; + } + + menuItem.title = NSLocalizedString(localizationKeyWithoutRemote, @""); + return NO; +} + + +- (void) windowDidLoad +{ + [super windowDidLoad]; + + // Explicitly set the frame using the autosave name + // Opening the first and second documents works fine, but the third and subsequent windows aren't positioned correctly + [[self window] setFrameUsingName:@"GitX"]; + [[self window] setRepresentedURL:self.repository.workingDirectoryURL]; + + sidebarController = [[PBGitSidebarController alloc] initWithRepository:self.repository superController:self]; + _historyViewController = [[PBGitHistoryController alloc] initWithRepository:self.repository superController:self]; + _commitViewController = [[PBGitCommitController alloc] initWithRepository:self.repository superController:self]; + + [[sidebarController view] setFrame:[sourceSplitView bounds]]; + [sourceSplitView addSubview:[sidebarController view]]; + [sourceListControlsView addSubview:sidebarController.sourceListControlsView]; + + [[statusField cell] setBackgroundStyle:NSBackgroundStyleRaised]; + [progressIndicator setUsesThreadedAnimation:YES]; +} + +- (void) removeAllContentSubViews +{ + if ([contentSplitView subviews]) + while ([[contentSplitView subviews] count] > 0) + [[[contentSplitView subviews] lastObject] removeFromSuperviewWithoutNeedingDisplay]; +} + +- (void) changeContentController:(PBViewController *)controller +{ + if (!controller || (contentController == controller)) + return; + + if (contentController) + [contentController removeObserver:self forKeyPath:@"status"]; + + [self removeAllContentSubViews]; + + contentController = controller; + + [[contentController view] setFrame:[contentSplitView bounds]]; + [contentSplitView addSubview:[contentController view]]; + +// [self setNextResponder: contentController]; + [[self window] makeFirstResponder:[contentController firstResponder]]; + [contentController updateView]; + [contentController addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionInitial context:@"statusChange"]; +} + +- (void)showCommitView:(id)sender +{ + segmentedControl.integerValue = 1; + [sidebarController selectStage]; +} + +- (void)showHistoryView:(id)sender +{ + segmentedControl.integerValue = 0; + [sidebarController selectCurrentBranch]; +} + +- (IBAction)segmentedControlValueChanged:(NSSegmentedControl *)sender { + if (sender.integerValue == 0) { + [self showHistoryView:sender]; + } else { + [self showCommitView:sender]; + } +} + +- (void)showCommitHookFailedSheet:(NSString *)messageText infoText:(NSString *)infoText commitController:(PBGitCommitController *)controller +{ + [PBCommitHookFailedSheet beginWithMessageText:messageText + infoText:infoText + commitController:controller + completionHandler:^(id _Nonnull sheet, NSModalResponse returnCode) { + if (returnCode != NSModalResponseOK) return; + + [self->_commitViewController forceCommit:self]; + }]; +} + +- (void)showMessageSheet:(NSString *)messageText infoText:(NSString *)infoText +{ + [PBGitXMessageSheet beginSheetWithMessage:messageText info:infoText windowController:self]; +} + +- (void)showErrorSheet:(NSError *)error +{ + if ([[error domain] isEqualToString:PBGitXErrorDomain]) + { + [PBGitXMessageSheet beginSheetWithError:error windowController:self]; + } + else + { + [[NSAlert alertWithError:error] beginSheetModalForWindow:[self window] + modalDelegate:self + didEndSelector:nil + contextInfo:nil]; + } +} + +- (void) updateStatus +{ + NSString *status = contentController.status; + BOOL isBusy = contentController.isBusy; + + if (!status) { + status = @""; + isBusy = NO; + } + + [statusField setStringValue:status]; + + if (isBusy) { + [progressIndicator startAnimation:self]; + [progressIndicator setHidden:NO]; + } + else { + [progressIndicator stopAnimation:self]; + [progressIndicator setHidden:YES]; + } +} + +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([(__bridge NSString *)context isEqualToString:@"statusChange"]) { + [self updateStatus]; + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (void)setHistorySearch:(NSString *)searchString mode:(PBHistorySearchMode)mode +{ + [_historyViewController setHistorySearch:searchString mode:mode]; +} + + + +- (void)openURLs:(NSArray *)fileURLs +{ + if (fileURLs.count == 0) return; + + NSMutableArray *nonSubmoduleURLs = [NSMutableArray array]; + + for (NSURL *fileURL in fileURLs) { + GTSubmodule *submodule = [self.repository submoduleAtPath:fileURL.path error:NULL]; + if (!submodule) { + [nonSubmoduleURLs addObject:fileURL]; + } else { + NSURL *submoduleURL = [submodule.parentRepository.fileURL URLByAppendingPathComponent:submodule.path isDirectory:YES]; + [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:submoduleURL + display:YES + completionHandler:^(NSDocument * _Nullable document, BOOL documentWasAlreadyOpen, NSError * _Nullable error) { + // Do nothing on completion. + return; + }]; + } + } + + [[NSWorkspace sharedWorkspace] openURLs:nonSubmoduleURLs + withAppBundleIdentifier:nil + options:0 + additionalEventParamDescriptor:nil + launchIdentifiers:NULL]; +} + +- (void)revealURLsInFinder:(NSArray *)fileURLs +{ + if (fileURLs.count == 0) return; + + [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:fileURLs]; +} + +- (void)performFetchForRef:(PBGitRef *)ref +{ + NSString *desc = nil; + if (ref == nil) { + desc = [NSString stringWithFormat:@"Fetching all remotes"]; + } else if (ref.isRemote || ref.isRemoteBranch) { + desc = [NSString stringWithFormat:@"Fetching branches from remote %@", ref.remoteName]; + } else { + desc = [NSString stringWithFormat:@"Fetching tracking branch for %@", ref.shortName]; + } + + PBRemoteProgressSheet *progressSheet = [PBRemoteProgressSheet progressSheetWithTitle:@"Fetching remote…" + description:desc + windowController:self]; + + [progressSheet beginProgressSheetForBlock:^{ + NSError *error = nil; + BOOL success = [self.repository fetchRemoteForRef:ref error:&error]; + return (success ? nil : error); + } completionHandler:^(NSError *error) { + if (error) { + [self showErrorSheet:error]; + } + }]; +} + +- (void)performPullForBranch:(PBGitRef *)branchRef remote:(PBGitRef *)remoteRef rebase:(BOOL)rebase { + NSString *description = nil; + if (!branchRef && !remoteRef) { + NSAssert(NO, @"Asked to pull no branch from no remote"); + } else if (!branchRef) { + description = [NSString stringWithFormat:@"Pulling all tracking branches from %@", remoteRef.remoteName]; + } else if (!remoteRef) { + description = [NSString stringWithFormat:@"Pulling default remote for branch %@", branchRef.shortName]; + } else { + description = [NSString stringWithFormat:@"Pulling branch %@ from remote %@", branchRef.shortName, remoteRef.remoteName]; + } + + PBRemoteProgressSheet *progressSheet = [PBRemoteProgressSheet progressSheetWithTitle:@"Pulling remote…" + description:description + windowController:self]; + + [progressSheet beginProgressSheetForBlock:^{ + NSError *error = nil; + BOOL success = [self.repository pullBranch:branchRef fromRemote:remoteRef rebase:rebase error:&error]; + return success ? nil : error; + } completionHandler:^(NSError *error) { + if (error) { + [self showErrorSheet:error]; + } + }]; +} + +- (void)performPushForBranch:(PBGitRef *)branchRef toRemote:(PBGitRef *)remoteRef +{ + if ((!branchRef && !remoteRef) + || (branchRef && !branchRef.isBranch && !branchRef.isRemoteBranch && !branchRef.isTag) + || (remoteRef && !remoteRef.isRemote)) + return; + + // This block is actually responsible for performing the push operation + void (^pushBlock)(void) = ^{ + NSString *description = nil; + if (branchRef && remoteRef) + description = [NSString stringWithFormat:@"Pushing %@ '%@' to remote %@", branchRef.refishType, branchRef.shortName, remoteRef.remoteName]; + else if (branchRef) + description = [NSString stringWithFormat:@"Pushing %@ '%@' to default remote", branchRef.refishType, branchRef.shortName]; + else + description = [NSString stringWithFormat:@"Pushing updates to remote %@", remoteRef.remoteName]; + + PBRemoteProgressSheet *progressSheet = [PBRemoteProgressSheet progressSheetWithTitle:@"Pushing remote…" + description:description + windowController:self]; + + [progressSheet beginProgressSheetForBlock:^{ + NSError *error = nil; + BOOL success = [self.repository pushBranch:branchRef toRemote:remoteRef error:&error]; + return (success ? nil : error); + } completionHandler:^(NSError *error) { + if (error) { + [self showErrorSheet:error]; + } + }]; + }; + + if ([PBGitDefaults isDialogWarningSuppressedForDialog:kDialogConfirmPush]) { + pushBlock(); + return; + } + + NSString *description = nil; + if (branchRef && remoteRef) + description = [NSString stringWithFormat:@"Push %@ '%@' to remote %@", branchRef.refishType, branchRef.shortName, remoteRef.remoteName]; + else if (branchRef) + description = [NSString stringWithFormat:@"Push %@ '%@' to default remote", branchRef.refishType, branchRef.shortName]; + else + description = [NSString stringWithFormat:@"Push updates to remote %@", remoteRef.remoteName]; + + NSString *sdesc = [NSString stringWithFormat:@"p%@", [description substringFromIndex:1]]; + NSAlert *alert = [[NSAlert alloc] init]; + alert.messageText = description; + alert.informativeText = [NSString stringWithFormat:@"Are you sure you want to %@?", sdesc]; + [alert addButtonWithTitle:NSLocalizedString(@"Push", @"")]; + [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"")]; + [alert setShowsSuppressionButton:YES]; + + [alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) { + if ([alert.suppressionButton state] == NSOnState) + [PBGitDefaults suppressDialogWarningForDialog:kDialogConfirmPush]; + + if (returnCode != NSAlertFirstButtonReturn) return; + + pushBlock(); + }]; +} + +- (NSArray *)selectedURLsFromSender:(id)sender { + NSArray *selectedFiles = [sender representedObject]; + if (![selectedFiles isKindOfClass:[NSArray class]] || [selectedFiles count] == 0) + return nil; + + NSMutableArray *URLs = [NSMutableArray array]; + for (id file in selectedFiles) { + NSString *path = file; + // Those can be PBChangedFiles sent by PBGitIndexController. Get their path. + if ([file respondsToSelector:@selector(path)]) { + path = [file path]; + } + + if (![path isKindOfClass:[NSString class]]) + continue; + [URLs addObject:[self.repository.workingDirectoryURL URLByAppendingPathComponent:path]]; + } + + return URLs; +} + +#pragma mark IBActions + +- (id )refishForSender:(id)sender refishTypes:(NSArray *)types +{ + if ([sender isKindOfClass:[NSMenuItem class]]) { + id refish = nil; + if ([(refish = [(NSMenuItem *)sender representedObject]) conformsToProtocol:@protocol(PBGitRefish)]) { + if (!types || [types indexOfObject:[refish refishType]] != NSNotFound) + return refish; + } + NSString *remoteName = nil; + if ([(remoteName = [(NSMenuItem *)sender representedObject]) isKindOfClass:[NSString class]]) { + if ([types indexOfObject:kGitXRemoteType] != NSNotFound + && [self.repository.remotes indexOfObject:remoteName] != NSNotFound) { + return [PBGitRef refFromString:[kGitXRemoteRefPrefix stringByAppendingString:remoteName]]; + } + } + + return nil; + } + + if ([types indexOfObject:kGitXCommitType] == NSNotFound) + return nil; + + return _historyViewController.selectedCommits.firstObject; +} + +- (PBGitRef *)selectedRef { + id firstResponder = self.window.firstResponder; + if (firstResponder == sidebarController.sourceView) { + NSOutlineView *sourceView = sidebarController.sourceView; + PBSourceViewItem *item = [sourceView itemAtRow:sourceView.selectedRow]; + PBGitRef *ref = item.ref; + if (ref && (item.parent == sidebarController.remotes)) { + ref = [PBGitRef refFromString:[kGitXRemoteRefPrefix stringByAppendingString:item.title]]; + } + return ref; + } else if (firstResponder == _historyViewController.commitList && _historyViewController.singleCommitSelected) { + NSMutableArray *branchCommits = [NSMutableArray array]; + for (PBGitRef *ref in _historyViewController.selectedCommits.firstObject.refs) { + if (!ref.isBranch) continue; + [branchCommits addObject:ref]; + } + return (branchCommits.count == 1 ? branchCommits.firstObject : nil); + } + return nil; +} + +- (IBAction) showAddRemoteSheet:(id)sender +{ + [self addRemote:sender]; +} + +- (IBAction)addRemote:(id)sender +{ + [PBAddRemoteSheet beginSheetWithWindowController:self completionHandler:^(PBAddRemoteSheet *addSheet, NSModalResponse returnCode) { + if (returnCode != NSModalResponseOK) return; + + NSString *remoteName = addSheet.remoteName.stringValue; + NSString *remoteURL = addSheet.remoteURL.stringValue; + + NSString *description = [NSString stringWithFormat:@"Adding remote \"%@\"", remoteName]; + + PBRemoteProgressSheet *progressSheet = [PBRemoteProgressSheet progressSheetWithTitle:@"Adding remote" + description:description + windowController:self]; + [progressSheet beginProgressSheetForBlock:^{ + NSError *error = nil; + BOOL success = [self.repository addRemote:remoteName withURL:remoteURL error:&error]; + return success ? nil : error; + } completionHandler:^(NSError *error) { + if (error) { + [self showErrorSheet:error]; + return; + } + + // Now fetch that remote + PBGitRef *remoteRef = [self.repository refForName:remoteName]; + [self performFetchForRef:remoteRef]; + }]; + }]; +} + +- (IBAction)deleteRef:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType, kGitXRemoteType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + + PBGitRef *ref = (PBGitRef *)refish; + + void (^performDelete)(void) = ^{ + NSError *error = nil; + BOOL success = [self.repository deleteRef:ref error:&error]; + if (!success) { + [self showErrorSheet:error]; + } + return; + }; + + if ([PBGitDefaults isDialogWarningSuppressedForDialog:kDialogDeleteRef]) { + performDelete(); + return; + } + + NSString *ref_desc = [NSString stringWithFormat:@"%@ '%@'", [ref refishType], [ref shortName]]; + + NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"Delete %@?", ref_desc] + defaultButton:@"Delete" + alternateButton:@"Cancel" + otherButton:nil + informativeTextWithFormat:@"Are you sure you want to remove the %@?", ref_desc]; + [alert setShowsSuppressionButton:YES]; + + [alert beginSheetModalForWindow:self.window + completionHandler:^(NSModalResponse returnCode) { + if ([[alert suppressionButton] state] == NSOnState) + [PBGitDefaults suppressDialogWarningForDialog:kDialogDeleteRef]; + + if (returnCode == NSModalResponseOK) { + performDelete(); + } + }]; +} + +- (IBAction)fetchRemote:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType, kGitXRemoteType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + + [self performFetchForRef:refish]; +} + +- (IBAction) fetchAllRemotes:(id)sender +{ + [self performFetchForRef:nil]; +} + +- (IBAction)pullRemote:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + + [self performPullForBranch:refish remote:nil rebase:NO]; +} + +- (IBAction) pullRebaseRemote:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + + [self performPullForBranch:refish remote:nil rebase:YES]; +} + +- (IBAction)pullDefaultRemote:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + + [self performPullForBranch:refish remote:nil rebase:NO]; +} + +- (IBAction)pullRebaseDefaultRemote:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + [self performPullForBranch:refish remote:nil rebase:YES]; +} + +- (IBAction)pushUpdatesToRemote:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXRemoteType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + + PBGitRef *remoteRef = [(PBGitRef *)refish remoteRef]; + + [self performPushForBranch:nil toRemote:remoteRef]; +} + +- (IBAction)pushDefaultRemoteForRef:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType]]; + if (!refish || ![refish isKindOfClass:[PBGitRef class]]) + return; + + PBGitRef *ref = (PBGitRef *)refish; + + [self performPushForBranch:ref toRemote:nil]; +} + +- (IBAction)pushToRemote:(id)sender +{ + NSMenuItem *remoteSubmenu = sender; + if (![remoteSubmenu isKindOfClass:[NSMenuItem class]]) return; + + id ref = [self refishForSender:remoteSubmenu.parentItem refishTypes:@[kGitXBranchType]]; + if (!ref || ![ref isKindOfClass:[PBGitRef class]]) + return; + + id remoteRef = [self refishForSender:sender refishTypes:@[kGitXRemoteType]]; + if (!remoteRef || ![remoteRef isKindOfClass:[PBGitRef class]]) + return; + + [self performPushForBranch:ref toRemote:remoteRef]; +} + +- (IBAction)checkout:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType, kGitXRemoteBranchType, kGitXCommitType, kGitXTagType]]; + if (!refish) return; + + NSError *error = nil; + BOOL success = [self.repository checkoutRefish:refish error:&error]; + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)merge:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXBranchType, kGitXRemoteBranchType, kGitXCommitType, kGitXTagType]]; + if (!refish) return; + + NSError *error = nil; + BOOL success = [self.repository mergeWithRefish:refish error:&error]; + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)rebase:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXCommitType]]; + if (!refish) return; + + NSError *error = nil; + BOOL success = [self.repository rebaseBranch:nil onRefish:refish error:&error]; + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)rebaseHeadBranch:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXCommitType]]; + if (!refish || ![refish isKindOfClass:[PBGitCommit class]]) + return; + + NSError *error = nil; + BOOL success = [self.repository rebaseBranch:nil onRefish:refish error:&error]; + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)cherryPick:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXCommitType]]; + if (!refish) return; + + NSError *error = nil; + BOOL success = [self.repository cherryPickRefish:refish error:&error]; + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)stashSave:(id)sender +{ + NSError *error = nil; + BOOL success = [self.repository stashSaveWithKeepIndex:NO error:&error]; + + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)stashSaveWithKeepIndex:(id) sender +{ + NSError *error = nil; + BOOL success = [self.repository stashSaveWithKeepIndex:YES error:&error]; + + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)stashPop:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXStashType]]; + PBGitStash *stash = [self.repository stashForRef:refish]; + if (!stash) { + stash = self.repository.stashes.firstObject; + } + + NSError *error = nil; + BOOL success = [self.repository stashPop:stash error:&error]; + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)stashApply:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXStashType]]; + PBGitStash *stash = [self.repository stashForRef:refish]; + NSError *error = nil; + BOOL success = [self.repository stashApply:stash error:&error]; + + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction)stashDrop:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXStashType]]; + PBGitStash *stash = [self.repository stashForRef:refish]; + NSError *error = nil; + BOOL success = [self.repository stashDrop:stash error:&error]; + + if (!success) { + [self showErrorSheet:error]; + } +} + +- (IBAction) openFiles:(id)sender { + NSArray *fileURLs = [self selectedURLsFromSender:sender]; + [self openURLs:fileURLs]; +} + +- (IBAction) revealInFinder:(id)sender +{ + [self revealURLsInFinder:@[self.repository.workingDirectoryURL]]; +} + +- (IBAction)openInTerminal:(id)sender +{ + [PBTerminalUtil runCommand:@"git status" inDirectory:self.repository.workingDirectoryURL]; +} + +- (IBAction) refresh:(id)sender +{ + [contentController refresh:self]; +} + +- (void) createBranch:(id)sender +{ + PBGitRef *currentRef = [self.repository.currentBranch ref]; + + /* WIP: must check */ + id refish = [self refishForSender:sender refishTypes:nil]; + if (!refish) { + PBGitCommit *selectedCommit = _historyViewController.selectedCommits.firstObject; + if (!selectedCommit || [selectedCommit hasRef:currentRef]) { + refish = currentRef; + } else { + refish = selectedCommit; + } + } + + [PBCreateBranchSheet beginSheetWithRefish:refish windowController:self completionHandler:^(PBCreateBranchSheet *sheet, NSModalResponse returnCode) { + if (returnCode != NSModalResponseOK) return; + + NSError *error = nil; + BOOL success = [self.repository createBranch:[sheet.branchNameField stringValue] atRefish:sheet.startRefish error:&error]; + if (!success) { + [self showErrorSheet:error]; + return; + } + + [PBGitDefaults setShouldCheckoutBranch:sheet.shouldCheckoutBranch]; + + if (sheet.shouldCheckoutBranch) { + success = [self.repository checkoutRefish:sheet.selectedRef error:&error]; + if (!success) { + [self showErrorSheet:error]; + return; + } + } + }]; +} + +- (IBAction) createTag:(id)sender +{ + /* WIP: must check */ + id refish = [self refishForSender:sender refishTypes:nil]; + if (!refish) { + PBGitCommit *selectedCommit = _historyViewController.selectedCommits.firstObject; + if (selectedCommit) + refish = selectedCommit; + else + refish = self.repository.currentBranch.ref; + } + + [PBCreateTagSheet beginSheetWithRefish:refish windowController:self completionHandler:^(PBCreateTagSheet *sheet, NSModalResponse returnCode) { + if (returnCode != NSModalResponseOK) return; + + NSString *tagName = [sheet.tagNameField stringValue]; + NSString *message = [sheet.tagMessageText string]; + NSError *error = nil; + BOOL success = [self.repository createTag:tagName message:message atRefish:sheet.targetRefish error:&error]; + if (!success) { + [self showErrorSheet:error]; + } + }]; +} + +- (IBAction)diffWithHEAD:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:nil]; + if (!refish) + return; + + PBGitCommit *commit = [self.repository commitForRef:refish]; + + NSString *diff = [self.repository performDiff:commit against:nil forFiles:nil]; + + [PBDiffWindowController showDiff:diff]; +} + +- (IBAction)stashViewDiff:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXStashType]]; + PBGitStash *stash = [self.repository stashForRef:refish]; + [PBDiffWindowController showDiffWindowWithFiles:nil fromCommit:stash.ancestorCommit diffCommit:stash.commit]; +} + +- (IBAction)showTagInfoSheet:(id)sender +{ + id refish = [self refishForSender:sender refishTypes:@[kGitXTagType]]; + if (!refish) + return; + + PBGitRef *ref = (PBGitRef *)refish; + + NSError *error = nil; + NSString *tagName = [ref tagName]; + NSString *tagRef = [@"refs/tags/" stringByAppendingString:tagName]; + GTObject *object = [self.repository.gtRepo lookUpObjectByRevParse:tagRef error:&error]; + if (!object) { + NSLog(@"Couldn't look up ref %@:%@", tagRef, [error debugDescription]); + return; + } + NSString *title = [NSString stringWithFormat:@"Info for tag: %@", tagName]; + NSString *info = @""; + if ([object isKindOfClass:[GTTag class]]) { + GTTag *tag = (GTTag*)object; + info = tag.message; + } + + [self showMessageSheet:title infoText:info]; +} + +@end diff --git a/Classes/Controllers/PBHistorySearchController.h b/Classes/Controllers/PBHistorySearchController.h new file mode 100644 index 000000000..61217ab96 --- /dev/null +++ b/Classes/Controllers/PBHistorySearchController.h @@ -0,0 +1,48 @@ +// +// PBHistorySearchController.h +// GitX +// +// Created by Nathan Kinsinger on 8/21/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBHistorySearchMode.h" + +@class PBGitHistoryController; +@class PBTask; + +@interface PBHistorySearchController : NSObject { + PBHistorySearchMode searchMode; + NSIndexSet *results; + NSTimer *searchTimer; + PBTask *backgroundSearchTask; + NSPanel *rewindPanel; +} + +@property (weak) IBOutlet PBGitHistoryController *historyController; +@property (weak) IBOutlet NSArrayController *commitController; + +@property (weak) IBOutlet NSSearchField *searchField; +@property (weak) IBOutlet NSSegmentedControl *stepper; +@property (weak) IBOutlet NSTextField *numberOfMatchesField; +@property (weak) IBOutlet NSProgressIndicator *progressIndicator; + +@property PBHistorySearchMode searchMode; + + +- (BOOL)isRowInSearchResults:(NSInteger)rowIndex; +- (BOOL)hasSearchResults; + +- (void)selectSearchMode:(id)sender; + +- (void)selectNextResult; +- (void)selectPreviousResult; +- (IBAction)stepperPressed:(id)sender; + +- (void)clearSearch; +- (IBAction)updateSearch:(id)sender; + +- (void)setHistorySearch:(NSString *)searchString mode:(PBHistorySearchMode)mode; + +@end diff --git a/Classes/Controllers/PBHistorySearchController.m b/Classes/Controllers/PBHistorySearchController.m new file mode 100644 index 000000000..db08d5de1 --- /dev/null +++ b/Classes/Controllers/PBHistorySearchController.m @@ -0,0 +1,569 @@ +// +// PBHistorySearchController.m +// GitX +// +// Created by Nathan Kinsinger on 8/21/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + +#import "PBHistorySearchController.h" +#import "PBGitHistoryController.h" +#import "PBGitRepository.h" +#import "PBGitRepository_PBGitBinarySupport.h" +#import "PBGitDefaults.h" +#import "PBCommitList.h" +#import "PBGitCommit.h" +#import "PBError.h" + +@interface PBHistorySearchController () + +- (void)selectNextResultInDirection:(NSInteger)direction; + +- (void)updateUI; +- (void)setupSearchMenuTemplate; + +- (void)startBasicSearch; +- (void)startBackgroundSearch; +- (void)clearProgressIndicator; + +- (void)showSearchRewindPanelReverse:(BOOL)isReversed; + +@end + + +#define kGitXSearchDirectionNext 1 +#define kGitXSearchDirectionPrevious -1 + +#define kGitXBasicSearchLabel NSLocalizedString(@"Subject, Author, SHA", @"Option in Search menu to search for subject, author or SHA") +#define kGitXPickaxeSearchLabel NSLocalizedString(@"Commit (pickaxe)", @"Option in Search menu to use the pickaxe search") +#define kGitXRegexSearchLabel NSLocalizedString(@"Commit (pickaxe regex)", @"Option in Search menu to use the pickaxe search with regular expressions") +#define kGitXPathSearchLabel NSLocalizedString(@"File path", @"Option in Search menu to search for file paths in the commit") + +#define kGitXSearchArrangedObjectsContext @"GitXSearchArrangedObjectsContext" + + +@implementation PBHistorySearchController + +@synthesize historyController; +@synthesize commitController; + +@synthesize searchField; +@synthesize stepper; +@synthesize numberOfMatchesField; +@synthesize progressIndicator; + + +#pragma mark - +#pragma mark Public methods + +- (BOOL)isRowInSearchResults:(NSInteger)rowIndex +{ + return [results containsIndex:rowIndex]; +} + +- (BOOL)hasSearchResults +{ + return ([results count] > 0); +} + +- (void)selectSearchMode:(id)sender +{ + [self setSearchMode:PBSearchModeForInteger([(NSView*)sender tag])]; + [self updateSearch:self]; +} + +- (void)selectNextResult +{ + [self selectNextResultInDirection:kGitXSearchDirectionNext]; +} + +- (void)selectPreviousResult +{ + [self selectNextResultInDirection:kGitXSearchDirectionPrevious]; +} + +- (IBAction)stepperPressed:(id)sender +{ + NSInteger selectedSegment = [sender selectedSegment]; + + if (selectedSegment == 0) + [self selectPreviousResult]; + else + [self selectNextResult]; +} + +- (void)clearSearch +{ + [searchField setStringValue:@""]; + if (results) { + results = nil; + [historyController.commitList reloadData]; + } + [self updateUI]; +} + +- (IBAction)updateSearch:(id)sender +{ + if (self.searchMode == PBHistorySearchModeBasic) + [self startBasicSearch]; + else + [self startBackgroundSearch]; +} + +- (void)setHistorySearch:(NSString *)searchString mode:(PBHistorySearchMode)mode +{ + if (searchString && ![searchString isEqualToString:@""]) { + self.searchMode = mode; + [searchField setStringValue:searchString]; + // use performClick: so that the search field will save it as a recent search + [searchField performClick:self]; + } +} + +- (void)awakeFromNib +{ + [self setupSearchMenuTemplate]; + self.searchMode = PBSearchModeForInteger([PBGitDefaults historySearchMode]); + + [self updateUI]; + + [commitController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:kGitXSearchArrangedObjectsContext]; +} + +- (void)dealloc { + [commitController removeObserver:self forKeyPath:@"arrangedObjects" context:kGitXSearchArrangedObjectsContext]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([(__bridge NSString *)context isEqualToString:kGitXSearchArrangedObjectsContext]) { + // the objects in the commitlist changed so the result indexes are no longer valid + [self clearSearch]; + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + + + +#pragma mark - +#pragma mark Private methods + +- (void)selectIndex:(NSUInteger)index +{ + if ([[commitController arrangedObjects] count] > index) { + PBGitCommit *commit = [[commitController arrangedObjects] objectAtIndex:index]; + [historyController selectCommit:commit.OID]; + } +} + +- (void)selectNextResultInDirection:(NSInteger)direction +{ + if (![results count]) + return; + + NSUInteger selectedRow = [historyController.commitList selectedRow]; + if (selectedRow == NSNotFound) { + [self selectIndex:[results firstIndex]]; + return; + } + + NSUInteger currentResult = NSNotFound; + if (direction == kGitXSearchDirectionNext) + currentResult = [results indexGreaterThanIndex:selectedRow]; + else + currentResult = [results indexLessThanIndex:selectedRow]; + + if (currentResult == NSNotFound) { + if (direction == kGitXSearchDirectionNext) + currentResult = [results firstIndex]; + else + currentResult = [results lastIndex]; + + [self showSearchRewindPanelReverse:(direction != kGitXSearchDirectionNext)]; + } + + [self selectIndex:currentResult]; +} + +- (NSString *)numberOfMatchesString +{ + NSUInteger numberOfMatches = [results count]; + + if (numberOfMatches == 0) + return NSLocalizedString(@"Not found", @"Search count (left of search field): no results"); + + if (numberOfMatches == 1) + return NSLocalizedString(@"1 match", @"Search count (left of search field): exactly one result"); + + return [NSString stringWithFormat: + NSLocalizedString(@"%lu matches", @"Search count (left of search field): number of results"), + numberOfMatches]; +} + +- (void)updateUI +{ + if ([[searchField stringValue] isEqualToString:@""]) { + [numberOfMatchesField setHidden:YES]; + [stepper setHidden:YES]; + } + else { + [numberOfMatchesField setStringValue:[self numberOfMatchesString]]; + [numberOfMatchesField setHidden:NO]; + [stepper setHidden:NO]; + [historyController.commitList reloadData]; + } + [self clearProgressIndicator]; +} + +// changes the selection to the next match after the current selected row unless the current row is already a match +- (void)updateSelectedResult +{ + NSString *searchString = [searchField stringValue]; + if ([searchString isEqualToString:@""]) { + [self clearSearch]; + return; + } + + if (![self isRowInSearchResults:[historyController.commitList selectedRow]]) + [self selectNextResult]; + + [self updateUI]; +} + +- (void)setupSearchMenuTemplate +{ + NSMenu *searchMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Search Menu", @"Title of the Search menu.")]; + NSMenuItem *item; + + item = [[NSMenuItem alloc] initWithTitle:kGitXBasicSearchLabel action:@selector(selectSearchMode:) keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:PBHistorySearchModeBasic]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:kGitXPickaxeSearchLabel action:@selector(selectSearchMode:) keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:PBHistorySearchModePickaxe]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:kGitXRegexSearchLabel action:@selector(selectSearchMode:) keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:PBHistorySearchModeRegex]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:kGitXPathSearchLabel action:@selector(selectSearchMode:) keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:PBHistorySearchModePath]; + [searchMenu addItem:item]; + + item = [NSMenuItem separatorItem]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Recent Searches", @"Searches menu: title of inactive headline item for Recent Searches section") action:NULL keyEquivalent:@""]; + [item setTag:NSSearchFieldRecentsTitleMenuItemTag]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""]; + [item setTag:NSSearchFieldRecentsMenuItemTag]; + [searchMenu addItem:item]; + + item = [NSMenuItem separatorItem]; + [item setTag:NSSearchFieldRecentsTitleMenuItemTag]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Clear Recent Searches", @"Searches menu: title of clear recent searches item") action:NULL keyEquivalent:@""]; + [item setTag:NSSearchFieldClearRecentsMenuItemTag]; + [searchMenu addItem:item]; + + item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"No Recent Searches", @"Searches menu: title of dummy item displayed in recent searches section when there are no recent searches") action:NULL keyEquivalent:@""]; + [item setTag:NSSearchFieldNoRecentsMenuItemTag]; + [searchMenu addItem:item]; + + [[searchField cell] setSearchMenuTemplate:searchMenu]; +} + +- (void)updateSearchMenuState +{ + NSMenu *searchMenu = [[searchField cell] searchMenuTemplate]; + if (!searchMenu) + return; + + [self updateSearchModeMenuItemWithTag:PBHistorySearchModeBasic inMenu:searchMenu]; + [self updateSearchModeMenuItemWithTag:PBHistorySearchModePickaxe inMenu:searchMenu]; + [self updateSearchModeMenuItemWithTag:PBHistorySearchModeRegex inMenu:searchMenu]; + [self updateSearchModeMenuItemWithTag:PBHistorySearchModePath inMenu:searchMenu]; + + [[searchField cell] setSearchMenuTemplate:searchMenu]; + + [PBGitDefaults setHistorySearchMode:searchMode]; +} + +- (void) updateSearchModeMenuItemWithTag:(PBHistorySearchMode)menuItemSearchMode inMenu:(NSMenu *) searchMenu { + NSMenuItem * menuItem = [searchMenu itemWithTag:menuItemSearchMode]; + [menuItem setState:(searchMode == menuItemSearchMode) ? NSOnState : NSOffState]; +} + +- (void)updateSearchPlaceholderString +{ + switch (self.searchMode) { + case PBHistorySearchModePickaxe: + [[searchField cell] setPlaceholderString:kGitXPickaxeSearchLabel]; + break; + case PBHistorySearchModeRegex: + [[searchField cell] setPlaceholderString:kGitXRegexSearchLabel]; + break; + case PBHistorySearchModePath: + [[searchField cell] setPlaceholderString:kGitXPathSearchLabel]; + break; + default: + [[searchField cell] setPlaceholderString:kGitXBasicSearchLabel]; + break; + } +} + +- (PBHistorySearchMode)searchMode +{ + return searchMode; +} + +- (void)setSearchMode:(PBHistorySearchMode)mode +{ + searchMode = mode; + [PBGitDefaults setHistorySearchMode:mode]; + + [self updateSearchMenuState]; + [self updateSearchPlaceholderString]; +} + +- (void)searchTimerFired:(NSTimer*)theTimer +{ + [self.progressIndicator setHidden:NO]; + [self.progressIndicator startAnimation:self]; +} + +- (void)clearProgressIndicator +{ + [searchTimer invalidate]; + searchTimer = nil; + [self.progressIndicator setHidden:YES]; + [self.progressIndicator stopAnimation:self]; +} + +- (void)startProgressIndicator +{ + [self clearProgressIndicator]; + [numberOfMatchesField setHidden:YES]; + [stepper setHidden:YES]; + searchTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(searchTimerFired:) userInfo:nil repeats:NO]; +} + + + +#pragma mark Basic Search + +- (void)startBasicSearch +{ + NSString *searchString = [searchField stringValue]; + if ([searchString isEqualToString:@""]) { + [self clearSearch]; + return; + } + + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; + NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"subject CONTAINS[cd] %@ OR author CONTAINS[cd] %@ OR SHA BEGINSWITH[c] %@", searchString, searchString, searchString]; + + NSUInteger index = 0; + for (PBGitCommit *commit in [commitController arrangedObjects]) { + if ([searchPredicate evaluateWithObject:commit]) + [indexes addIndex:index]; + index++; + } + + results = indexes; + + [self updateSelectedResult]; +} + + + +#pragma mark Background Search + +- (void)startBackgroundSearch +{ + if (backgroundSearchTask) { + [backgroundSearchTask terminate]; + } + + NSString *searchString = [[searchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([searchString isEqualToString:@""]) { + [self clearSearch]; + return; + } + + results = nil; + + NSMutableArray *searchArguments = [NSMutableArray arrayWithObjects:@"log", @"--pretty=format:%H", @"--no-textconv", nil]; + switch (self.searchMode) { + case PBHistorySearchModeRegex: + [searchArguments addObject:@"--pickaxe-regex"]; + case PBHistorySearchModePickaxe: + [searchArguments addObject:[NSString stringWithFormat:@"-S%@", searchString]]; + break; + case PBHistorySearchModePath: + [searchArguments addObject:@"--"]; + [searchArguments addObjectsFromArray:[searchString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]; + break; + default: + return; + } + + backgroundSearchTask = [historyController.repository taskWithArguments:searchArguments]; + [backgroundSearchTask performTaskWithCompletionHandler:^(NSData *readData, NSError *taskError) { + if (taskError.domain == PBTaskErrorDomain && taskError.code == PBTaskCaughtSignalError) { + /* Silently ignore task termination */ + return; + } + + if (!readData) { + [self clearProgressIndicator]; + NSString *desc = NSLocalizedString(@"Search failed", @"Search Controller - Background search failed error description"); + NSString *reason = NSLocalizedString(@"The search for \"%@\" failed.", @"Search Controller - Background search failed error reason"); + reason = [NSString stringWithFormat:reason, searchString]; + NSError *error = [NSError pb_errorWithDescription:desc + failureReason:reason + underlyingError:taskError]; + [self->historyController.windowController showErrorSheet:error]; + return; + } + [self parseBackgroundSearchResults:readData]; + }]; + + [self startProgressIndicator]; +} + +- (void)parseBackgroundSearchResults:(NSData *)data +{ + backgroundSearchTask = nil; + + NSString *resultsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSArray *resultsArray = [resultsString componentsSeparatedByString:@"\n"]; + + NSMutableSet *matches = [NSMutableSet new]; + for (NSString *resultSHA in resultsArray) { + GTOID *resultOID = [GTOID oidWithSHA:resultSHA]; + if (resultOID) { + [matches addObject:resultOID]; + } + } + + NSArray *arrangedObjects = [commitController arrangedObjects]; + NSIndexSet *indexes = [arrangedObjects indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + PBGitCommit *commit = obj; + return [matches containsObject:commit.OID]; + }]; + + results = indexes; + [self clearProgressIndicator]; + [self updateSelectedResult]; +} + + + +#pragma mark - +#pragma mark Rewind Panel + +#define kRewindPanelSize 125.0f +#define kRewindPanelImageViewTag 1234 + +- (void)closeRewindPanel +{ + [[[historyController view] window] removeChildWindow:rewindPanel]; + [rewindPanel close]; + rewindPanel = nil; +} + +- (NSPanel *)rewindPanel +{ + // Update the panel frame in case the window was resized + NSRect windowFrame = [[[historyController view] window] frame]; + NSRect historyFrame = [historyController.view.window.contentView convertRect:historyController.view.frame + fromView:historyController.view]; + NSRect panelRect = NSMakeRect(0.0f, 0.0f, kRewindPanelSize, kRewindPanelSize); + panelRect.origin.x = windowFrame.origin.x + historyFrame.origin.x + ((historyFrame.size.width - kRewindPanelSize) / 2.0f); + panelRect.origin.y = windowFrame.origin.y + historyFrame.origin.y + ((historyFrame.size.height - kRewindPanelSize) / 2.0f); + + NSPanel *panel = [[NSPanel alloc] initWithContentRect:panelRect + styleMask:NSWindowStyleMaskBorderless + backing:NSBackingStoreBuffered + defer:YES]; + [panel setIgnoresMouseEvents:YES]; + [panel setOneShot:YES]; + [panel setOpaque:NO]; + [panel setBackgroundColor:[NSColor clearColor]]; + [panel setHasShadow:NO]; + [panel setAlphaValue:0.0f]; + + NSBox *box = [[NSBox alloc] initWithFrame:[[panel contentView] frame]]; + [box setBoxType:NSBoxCustom]; + [box setBorderType:NSLineBorder]; + [box setFillColor:[NSColor colorWithCalibratedWhite:0.0f alpha:0.5f]]; + [box setBorderColor:[NSColor colorWithCalibratedWhite:0.5f alpha:0.5f]]; + [box setCornerRadius:12.0f]; + [[panel contentView] addSubview:box]; + + NSImage *rewindImage = [NSImage imageNamed:@"rewindImage"]; + NSSize imageSize = [rewindImage size]; + NSRect imageViewFrame = NSMakeRect(21.0f, 5.0f, imageSize.width, imageSize.height); + NSImageView *rewindImageView = [[NSImageView alloc] initWithFrame:imageViewFrame]; + [rewindImageView setTag:kRewindPanelImageViewTag]; + [[box contentView] addSubview:rewindImageView]; + + return panel; +} + +- (CAKeyframeAnimation *)rewindPanelFadeOutAnimation +{ + CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; + animation.duration = 1.0f; + animation.values = @[@1.0f, @1.0f, @0.0f, @0.0f]; + animation.keyTimes = @[@0.1f, @0.3f, @0.7f, [NSNumber numberWithDouble:animation.duration]]; + return animation; +} + +- (void)showSearchRewindPanelReverse:(BOOL)isReversed +{ + if (rewindPanel != nil) { + // Panel still open, cancel the upcoming close + [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(closeRewindPanel) object:nil]; + } else { + // The panel is already closed, create a new one + rewindPanel = [self rewindPanel]; + + [[[historyController view] window] addChildWindow:rewindPanel ordered:NSWindowAbove]; + } + + // Setup our wrap-results image depending on the direction we wrapped + NSImage *rewindImage = [NSImage imageNamed:@"rewindImage"]; + NSImage *reversedRewindImage = [NSImage imageWithSize:rewindImage.size + flipped:isReversed + drawingHandler:^BOOL(NSRect destRect) { + [rewindImage drawInRect:destRect fromRect:NSZeroRect operation:NSCompositingOperationCopy fraction:1.0]; + return YES; + }]; + NSImageView *rewindImageView = [rewindPanel.contentView viewWithTag:kRewindPanelImageViewTag]; + [rewindImageView setImage:reversedRewindImage]; + + // Perform the fade-out animation + [rewindPanel setAlphaValue:1.0f]; + + CAKeyframeAnimation *alphaAnimation = [self rewindPanelFadeOutAnimation]; + [rewindPanel setAnimations:[NSDictionary dictionaryWithObject:alphaAnimation forKey:@"alphaValue"]]; + [[rewindPanel animator] setAlphaValue:0.0f]; + + [self performSelector:@selector(closeRewindPanel) withObject:nil afterDelay:0.7f]; +} + +@end diff --git a/Classes/Controllers/PBHistorySearchMode.h b/Classes/Controllers/PBHistorySearchMode.h new file mode 100644 index 000000000..361fbafd3 --- /dev/null +++ b/Classes/Controllers/PBHistorySearchMode.h @@ -0,0 +1,26 @@ +// +// PBHistorySearchMode.h +// GitX +// +// Created by Sven-S. Porst on 2016-12-14 +// + +#import + +#ifndef PBHistorySearchMode_h +#define PBHistorySearchMode_h + + +typedef NS_ENUM(NSInteger, PBHistorySearchMode) { + PBHistorySearchModeBasic = 1, + PBHistorySearchModePickaxe, + PBHistorySearchModeRegex, + PBHistorySearchModePath, + PBHistorySearchModeMax // always keep this item last +} ; + + +PBHistorySearchMode PBSearchModeForInteger(NSInteger modeInteger); + + +#endif /* PBHistorySearchMode_h */ diff --git a/Classes/Controllers/PBHistorySearchMode.m b/Classes/Controllers/PBHistorySearchMode.m new file mode 100644 index 000000000..34a295295 --- /dev/null +++ b/Classes/Controllers/PBHistorySearchMode.m @@ -0,0 +1,15 @@ +// +// PBHistorySearchMode.m +// GitX +// +// Created by Sven-S. Porst on 2016-12-14. +// + +#import "PBHistorySearchMode.h" + +PBHistorySearchMode PBSearchModeForInteger(NSInteger modeInteger) { + if (modeInteger >= PBHistorySearchModeBasic && modeInteger < PBHistorySearchModeMax) { + return (PBHistorySearchMode)modeInteger; + } + return PBHistorySearchModeBasic; +} diff --git a/Classes/Controllers/PBOpenShallowRepositoryErrorRecoveryAttempter.h b/Classes/Controllers/PBOpenShallowRepositoryErrorRecoveryAttempter.h new file mode 100644 index 000000000..0efd3a0f0 --- /dev/null +++ b/Classes/Controllers/PBOpenShallowRepositoryErrorRecoveryAttempter.h @@ -0,0 +1,17 @@ +// +// PBOpenShallowRepositoryErrorRecoveryAttempter.h +// GitX +// +// Created by Sven on 07.08.16. +// +// + +#import + +@interface PBOpenShallowRepositoryErrorRecoveryAttempter : NSObject + +- (instancetype) initWithURL:(NSURL *)url; + ++ (NSArray*) errorDialogButtonNames; + +@end \ No newline at end of file diff --git a/Classes/Controllers/PBOpenShallowRepositoryErrorRecoveryAttempter.m b/Classes/Controllers/PBOpenShallowRepositoryErrorRecoveryAttempter.m new file mode 100644 index 000000000..b18b787cd --- /dev/null +++ b/Classes/Controllers/PBOpenShallowRepositoryErrorRecoveryAttempter.m @@ -0,0 +1,40 @@ +// +// PBOpenShallowRepositoryErrorRecoveryAttempter.m +// GitX +// +// Created by Sven on 07.08.16. +// + +#import "PBOpenShallowRepositoryErrorRecoveryAttempter.h" +#import "PBTerminalUtil.h" + + +@implementation PBOpenShallowRepositoryErrorRecoveryAttempter + +NSURL * workingDirectory; + +- (instancetype) initWithURL:(NSURL *)url { + if (self != nil) { + workingDirectory = url; + } + return self; +} + +- (BOOL)attemptRecoveryFromError:(NSError *)error + optionIndex:(NSUInteger)recoveryOptionIndex { + + if (recoveryOptionIndex == 1) { + NSString * unshallowCommand = @"echo 'Please re-open the repository in GitX once unshallowing has finished.'; git fetch --unshallow"; + [PBTerminalUtil runCommand:unshallowCommand inDirectory:workingDirectory]; + return NO; + } + return NO; +} + ++ (NSArray*) errorDialogButtonNames { + return @[ + NSLocalizedString(@"OK", @"OK"), + NSLocalizedString(@"Run command in Terminal", @"Button to run unshallow command in Terminal")]; +} + +@end \ No newline at end of file diff --git a/Classes/Controllers/PBRefController.h b/Classes/Controllers/PBRefController.h new file mode 100644 index 000000000..730961008 --- /dev/null +++ b/Classes/Controllers/PBRefController.h @@ -0,0 +1,20 @@ +// +// PBRefController.h +// GitX +// +// Created by Pieter de Bie on 21-10-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import +#import "PBRefContextDelegate.h" + +@class PBGitHistoryController, PBCommitList; + +@interface PBRefController : NSObject { + __weak IBOutlet PBGitHistoryController *historyController; + __weak IBOutlet NSArrayController *commitController; + __weak IBOutlet PBCommitList *commitList; +} + +@end diff --git a/Classes/Controllers/PBRefController.m b/Classes/Controllers/PBRefController.m new file mode 100644 index 000000000..c69411bfa --- /dev/null +++ b/Classes/Controllers/PBRefController.m @@ -0,0 +1,198 @@ +// +// PBLabelController.m +// GitX +// +// Created by Pieter de Bie on 21-10-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import "PBRefController.h" +#import "PBGitRevisionCell.h" +#import "PBRefMenuItem.h" +#import "PBGitDefaults.h" +#import "PBDiffWindowController.h" +#import "PBGitRevSpecifier.h" +#import "PBGitStash.h" +#import "GitXCommitCopier.h" +#import "PBCommitList.h" +#import "PBGitHistoryController.h" + + + +@implementation PBRefController + +- (void)awakeFromNib +{ + [commitList registerForDraggedTypes:[NSArray arrayWithObject:@"PBGitRef"]]; +} + +#pragma mark Contextual menus + +- (NSArray *) menuItemsForRef:(PBGitRef *)ref +{ + return [NSMenuItem pb_defaultMenuItemsForRef:ref inRepository:historyController.repository]; +} + +- (NSArray *) menuItemsForCommits:(NSArray *)commits +{ + return [NSMenuItem pb_defaultMenuItemsForCommits:commits]; +} + +- (NSArray *)menuItemsForRow:(NSInteger)rowIndex +{ + NSArray *commits = [commitController arrangedObjects]; + if ([commits count] <= rowIndex) + return nil; + + return [self menuItemsForCommits:@[[commits objectAtIndex:rowIndex]]]; +} + + + +# pragma mark Tableview delegate methods + +- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard +{ + + NSPoint location = [(PBCommitList *)tv mouseDownPoint]; + NSInteger row = [tv rowAtPoint:location]; + NSInteger column = [tv columnAtPoint:location]; + + PBGitRevisionCell *cell = (PBGitRevisionCell *)[tv preparedCellAtColumn:column row:row]; + PBGitCommit *commit = [[commitController arrangedObjects] objectAtIndex:row]; + + int index = -1; + if ([cell respondsToSelector:@selector(indexAtX:)]) { + NSRect cellFrame = [tv frameOfCellAtColumn:column row:row]; + CGFloat deltaX = location.x - cellFrame.origin.x; + index = [cell indexAtX:deltaX]; + } + + if (index != -1) { + PBGitRef *ref = [[commit refs] objectAtIndex:index]; + if ([ref isTag] || [ref isRemoteBranch]) + return NO; + + if ([[[historyController.repository headRef] ref] isEqualToRef:ref]) + return NO; + + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[NSArray arrayWithObjects:[NSNumber numberWithInteger:row], [NSNumber numberWithInt:index], NULL]]; + [pboard declareTypes:[NSArray arrayWithObject:@"PBGitRef"] owner:self]; + [pboard setData:data forType:@"PBGitRef"]; + } else { + [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; + + NSString *info = nil; + if (column == [tv columnWithIdentifier:@"ShortSHAColumn"]) { + info = [commit shortName]; + } else { + info = [NSString stringWithFormat:@"%@ (%@)", [commit shortName], [commit subject]]; + } + + [pboard setString:info forType:NSStringPboardType]; + } + + return YES; +} + +- (NSDragOperation)tableView:(NSTableView*)tv + validateDrop:(id )info + proposedRow:(NSInteger)row + proposedDropOperation:(NSTableViewDropOperation)operation +{ + if (operation == NSTableViewDropAbove) + return NSDragOperationNone; + + NSPasteboard *pboard = [info draggingPasteboard]; + if ([pboard dataForType:@"PBGitRef"]) + return NSDragOperationMove; + + return NSDragOperationNone; +} + +- (void) dropRef:(NSDictionary *)dropInfo +{ + PBGitRef *ref = [dropInfo objectForKey:@"dragRef"]; + PBGitCommit *oldCommit = [dropInfo objectForKey:@"oldCommit"]; + PBGitCommit *dropCommit = [dropInfo objectForKey:@"dropCommit"]; + if (!ref || ! oldCommit || !dropCommit) + return; + + if (![historyController.repository updateReference:ref toPointAtCommit:dropCommit]) + + [dropCommit addRef:ref]; + [oldCommit removeRef:ref]; + [historyController.commitList reloadData]; +} + +- (BOOL)tableView:(NSTableView *)aTableView + acceptDrop:(id )info + row:(NSInteger)row + dropOperation:(NSTableViewDropOperation)operation +{ + if (operation != NSTableViewDropOn) + return NO; + + NSPasteboard *pboard = [info draggingPasteboard]; + NSData *data = [pboard dataForType:@"PBGitRef"]; + if (!data) + return NO; + + NSArray *numbers = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + int oldRow = [[numbers objectAtIndex:0] intValue]; + if (oldRow == row) + return NO; + + int oldRefIndex = [[numbers objectAtIndex:1] intValue]; + PBGitCommit *oldCommit = [[commitController arrangedObjects] objectAtIndex:oldRow]; + PBGitRef *ref = [[oldCommit refs] objectAtIndex:oldRefIndex]; + + PBGitCommit *dropCommit = [[commitController arrangedObjects] objectAtIndex:row]; + + NSDictionary *dropInfo = [NSDictionary dictionaryWithObjectsAndKeys: + ref, @"dragRef", + oldCommit, @"oldCommit", + dropCommit, @"dropCommit", + nil]; + + if ([PBGitDefaults isDialogWarningSuppressedForDialog:kDialogAcceptDroppedRef]) { + [self dropRef:dropInfo]; + return YES; + } + + NSString *subject = [dropCommit subject]; + if ([subject length] > 99) + subject = [[subject substringToIndex:99] stringByAppendingString:@"…"]; + NSString *infoText = [NSString stringWithFormat:@"Move the %@ to point to the commit: %@", [ref refishType], subject]; + + NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"Move %@: %@", [ref refishType], [ref shortName]] + defaultButton:@"Move" + alternateButton:@"Cancel" + otherButton:nil + informativeTextWithFormat:@"%@", infoText]; + [alert setShowsSuppressionButton:YES]; + + [alert beginSheetModalForWindow:[historyController.windowController window] + modalDelegate:self + didEndSelector:@selector(acceptDropInfoAlertDidEnd:returnCode:contextInfo:) + contextInfo:(__bridge_retained void*)dropInfo]; + + return YES; +} + +- (void)acceptDropInfoAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + [[alert window] orderOut:nil]; + + if (returnCode == NSAlertDefaultReturn) + [self dropRef:(__bridge NSDictionary*)contextInfo]; + + if ([[alert suppressionButton] state] == NSOnState) + [PBGitDefaults suppressDialogWarningForDialog:kDialogAcceptDroppedRef]; +} + +- (void)dealloc { + historyController = nil; +} + +@end diff --git a/PBRepositoryDocumentController.h b/Classes/Controllers/PBRepositoryDocumentController.h similarity index 76% rename from PBRepositoryDocumentController.h rename to Classes/Controllers/PBRepositoryDocumentController.h index d0660e241..7cfec4160 100644 --- a/PBRepositoryDocumentController.h +++ b/Classes/Controllers/PBRepositoryDocumentController.h @@ -7,13 +7,6 @@ // #import -#import "PBGitRevSpecifier.h" - @interface PBRepositoryDocumentController : NSDocumentController -{ - -} - -- (id) documentForLocation:(NSURL*) url; @end diff --git a/Classes/Controllers/PBRepositoryDocumentController.m b/Classes/Controllers/PBRepositoryDocumentController.m new file mode 100644 index 000000000..b49bc1119 --- /dev/null +++ b/Classes/Controllers/PBRepositoryDocumentController.m @@ -0,0 +1,58 @@ +// +// PBRepositoryDocumentController.mm +// GitX +// +// Created by Ciarán Walsh on 15/08/2008. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBRepositoryDocumentController.h" +#import "PBGitRepositoryDocument.h" +#import "PBGitRevList.h" +#import "PBGitBinary.h" + +#import + +@implementation PBRepositoryDocumentController +// This method is overridden to configure the open panel to only allow +// selection of directories +- (void)beginOpenPanel:(NSOpenPanel *)openPanel forTypes:(NSArray *)inTypes completionHandler:(void (^)(NSInteger))completionHandler { + [openPanel setCanChooseFiles:YES]; + [openPanel setCanChooseDirectories:YES]; + [openPanel setAllowedFileTypes:[NSArray arrayWithObject:@"git"]]; + + NSModalResponse response = [openPanel runModal]; + + completionHandler(response); +} + +- (id)makeUntitledDocumentOfType:(NSString *)typeName error:(NSError *__autoreleasing *)outError { + NSOpenPanel *op = [NSOpenPanel openPanel]; + + [op setCanChooseFiles:NO]; + [op setCanChooseDirectories:YES]; + [op setAllowsMultipleSelection:NO]; + [op setMessage:NSLocalizedString(@"Initialize a repository here:", @"Message at the top of the repository initialisation file selection dialogue box")]; + [op setTitle:NSLocalizedString(@"New Repository", @"Title of the repository initialisation file selection dialogue box")]; + if ([op runModal] != NSFileHandlingPanelOKButton) { + if (outError) { + *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]; + } + return nil; + } + + GTRepository *repo = [GTRepository initializeEmptyRepositoryAtFileURL:[op URL] options:nil error:outError]; + if (!repo) + return nil; // Repo creation failed + + return [[PBGitRepositoryDocument alloc] initWithContentsOfURL:[op URL] ofType:PBGitRepositoryDocumentType error:outError]; +} + +- (BOOL)validateMenuItem:(NSMenuItem *)item +{ + if ([item action] == @selector(newDocument:)) + return ([PBGitBinary path] != nil); + return YES; +} + +@end diff --git a/PBServicesController.h b/Classes/Controllers/PBServicesController.h similarity index 74% rename from PBServicesController.h rename to Classes/Controllers/PBServicesController.h index 4069cd0d4..39af33082 100644 --- a/PBServicesController.h +++ b/Classes/Controllers/PBServicesController.h @@ -9,11 +9,8 @@ #import -@interface PBServicesController : NSObject { - -} - -- (NSString *)completeSHA1For:(NSString *)sha; +@interface PBServicesController : NSObject - (void)completeSha:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error; + @end diff --git a/Classes/Controllers/PBServicesController.m b/Classes/Controllers/PBServicesController.m new file mode 100644 index 000000000..9cca00771 --- /dev/null +++ b/Classes/Controllers/PBServicesController.m @@ -0,0 +1,74 @@ +// +// PBServicesController.m +// GitX +// +// Created by Pieter de Bie on 10/24/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBServicesController.h" +#import "PBGitRepositoryDocument.h" +#import "PBGitRepository.h" +#import "PBGitRepository_PBGitBinarySupport.h" + +@implementation PBServicesController + +- (NSString *)completeSHA1For:(NSString *)sha error:(NSString **)error +{ + NSArray *documents = [[NSApplication sharedApplication] orderedDocuments]; + for (PBGitRepositoryDocument *doc in documents) + { + NSError *error = nil; + NSString *s = [doc.repository outputOfTaskWithArguments:@[@"log", @"-1", @"--pretty=format:%h (%s)", sha] error:&error]; + if (s) { + return s; + } + } + + if (error) *error = @"Unable to resolve SHA in opened repositories"; + return nil; +} + +- (NSString *)runNameRevFor:(NSString *)s error:(NSString **)errorStr +{ + NSArray *repositories = [[NSApplication sharedApplication] orderedDocuments]; + if ([repositories count] == 0) + return s; + PBGitRepositoryDocument *doc = [repositories objectAtIndex:0]; + + NSError *error = nil; + PBTask *task = [doc.repository taskWithArguments:@[]]; + task.standardInputData = [s dataUsingEncoding:NSUTF8StringEncoding]; + BOOL success = [task launchTask:&error]; + + if (success) { + return task.standardOutputString; + } else { + PBLogError(error); + } + + if (errorStr) *errorStr = @"Unable to resolve SHA in opened repositories"; + return nil; +} + +- (void)completeSha:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error +{ + NSArray *types = [pboard types]; + if (![types containsObject:NSStringPboardType]) + { + *error = @"Could not get data"; + return; + } + + NSString *s = [pboard stringForType:NSStringPboardType]; + if ([s rangeOfString:@" "].location == NSNotFound) + s = [self completeSHA1For:s error:error]; + else + s = [self runNameRevFor:s error:error]; + + if (!s) return; + + [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; + [pboard setString:s forType:NSStringPboardType]; +} +@end diff --git a/Classes/Controllers/PBViewController.h b/Classes/Controllers/PBViewController.h new file mode 100644 index 000000000..0d4818837 --- /dev/null +++ b/Classes/Controllers/PBViewController.h @@ -0,0 +1,36 @@ +// +// PBViewController.h +// GitX +// +// Created by Pieter de Bie on 22-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + +#import "PBGitRepository.h" +#import "PBGitWindowController.h" + +@interface PBViewController : NSViewController { + // FIXME: these ivars must go, but most controller out there access it directly, so, not today + PBGitRepository *repository; + __weak PBGitWindowController *superController; +} + +@property (nonatomic, strong, readonly) PBGitRepository *repository; +@property (weak, readonly) PBGitWindowController *windowController; +@property (copy) NSString *status; +@property (assign) BOOL isBusy; + +- (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller; + +/* closeView is called when the repository window will be closed */ +- (void)closeView; + +/* Updateview is called every time it is loaded into the main view */ +- (void) updateView; + +- (NSResponder *)firstResponder; +- (IBAction) refresh:(id)sender; + +@end diff --git a/PBViewController.m b/Classes/Controllers/PBViewController.m similarity index 51% rename from PBViewController.m rename to Classes/Controllers/PBViewController.m index 510b7a7f3..013937c6a 100644 --- a/PBViewController.m +++ b/Classes/Controllers/PBViewController.m @@ -8,41 +8,53 @@ #import "PBViewController.h" +@interface PBViewController () { + BOOL _hasViewLoaded; +} +@end @implementation PBViewController -@synthesize repository, viewToolbar; +@synthesize repository=repository; +@synthesize windowController=superController; - (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller { NSString *nibName = [[[self class] description] stringByReplacingOccurrencesOfString:@"Controller" withString:@"View"]; - if(self = [self initWithNibName:nibName bundle:nil]) { - repository = theRepository; - superController = controller; - } + self = [self initWithNibName:nibName bundle:nil]; + if (!self) return nil; + + repository = theRepository; + superController = controller; return self; } -- (void) removeView +- (void)closeView { [self unbind:@"repository"]; - [[self view] removeFromSuperview]; // remove the current view + if (_hasViewLoaded) + [[self view] removeFromSuperview]; // remove the current view } -- (void) awakeFromNib +- (void)awakeFromNib { + _hasViewLoaded = YES; } -// This is called when the view is displayed again; it -// should be updated to show the most recent information -- (void) updateView +- (NSResponder *)firstResponder; { + return nil; } -- (NSResponder *)firstResponder; +- (IBAction) refresh: sender { - return nil; } + +// The next methods should be implemented in the subclass if necessary +- (void)updateView +{ +} + @end diff --git a/PBWebChangesController.h b/Classes/Controllers/PBWebChangesController.h similarity index 85% rename from PBWebChangesController.h rename to Classes/Controllers/PBWebChangesController.h index 069f6fc9f..254ef1a40 100644 --- a/PBWebChangesController.h +++ b/Classes/Controllers/PBWebChangesController.h @@ -15,8 +15,8 @@ @interface PBWebChangesController : PBWebController { IBOutlet NSArrayController *unstagedFilesController; - IBOutlet NSArrayController *cachedFilesController; - IBOutlet PBGitCommitController *controller; + IBOutlet NSArrayController *stagedFilesController; + __weak IBOutlet PBGitCommitController *controller; IBOutlet PBGitIndexController *indexController; PBChangedFile *selectedFile; diff --git a/Classes/Controllers/PBWebChangesController.m b/Classes/Controllers/PBWebChangesController.m new file mode 100644 index 000000000..1e76069a4 --- /dev/null +++ b/Classes/Controllers/PBWebChangesController.m @@ -0,0 +1,176 @@ +// +// PBWebChangesController.m +// GitX +// +// Created by Pieter de Bie on 22-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBWebChangesController.h" +#import "PBGitIndex.h" + +@interface PBWebChangesController () +@end + +@implementation PBWebChangesController + +- (void) awakeFromNib +{ + selectedFile = nil; + selectedFileIsCached = NO; + + startFile = @"commit"; + [super awakeFromNib]; + + [unstagedFilesController addObserver:self forKeyPath:@"selection" options:0 context:@"UnstagedFileSelected"]; + [stagedFilesController addObserver:self forKeyPath:@"selection" options:0 context:@"cachedFileSelected"]; + + self.view.editingDelegate = self; + self.view.UIDelegate = self; +} + +- (void)closeView +{ + [[self script] removeWebScriptKey:@"Index"]; + [unstagedFilesController removeObserver:self forKeyPath:@"selection"]; + [stagedFilesController removeObserver:self forKeyPath:@"selection"]; + + [super closeView]; +} + +- (void) didLoad +{ + [[self script] setValue:controller.index forKey:@"Index"]; + [self refresh]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + NSArrayController *otherController; + otherController = object == unstagedFilesController ? stagedFilesController : unstagedFilesController; + NSUInteger count = [object selectedObjects].count; + if (count == 0) { + if([[otherController selectedObjects] count] == 0 && selectedFile) { + selectedFile = nil; + selectedFileIsCached = NO; + [self refresh]; + } + return; + } + + // TODO: Move this to commitcontroller + [otherController setSelectionIndexes:[NSIndexSet indexSet]]; + + if (count > 1) { + [self showMultiple: [object selectedObjects]]; + return; + } + + selectedFile = [[object selectedObjects] objectAtIndex:0]; + selectedFileIsCached = object == stagedFilesController; + + [self refresh]; +} + +- (void) showMultiple: (NSArray *)objects +{ + [[self script] callWebScriptMethod:@"showMultipleFilesSelection" withArguments:[NSArray arrayWithObject:objects]]; +} + +- (void) refresh +{ + if (!finishedLoading) + return; + + id script = self.view.windowScriptObject; + [script callWebScriptMethod:@"showFileChanges" + withArguments:[NSArray arrayWithObjects:selectedFile ?: (id)[NSNull null], + [NSNumber numberWithBool:selectedFileIsCached], nil]]; +} + +- (void)stageHunk:(NSString *)hunk reverse:(BOOL)reverse +{ + [controller.index applyPatch:hunk stage:YES reverse:reverse]; + // FIXME: Don't need a hard refresh + + [self refresh]; +} + +- (void) discardHunk:(NSString *)hunk +{ + [controller.index applyPatch:hunk stage:NO reverse:YES]; + [self refresh]; +} + +- (void) discardHunkAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + [[alert window] orderOut:nil]; + + if (returnCode == NSAlertDefaultReturn) + [self discardHunk:(__bridge NSString*)contextInfo]; +} + +- (void)discardHunk:(NSString *)hunk altKey:(BOOL)altKey +{ + if (!altKey) { + NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Discard hunk", @"Title of dialogue asking whether the user really wanted to press the Discard button on a hunk in the changes view") + defaultButton:nil + alternateButton:NSLocalizedString(@"Cancel", @"Cancel (discarding a hunk in the changes view)") + otherButton:nil + informativeTextWithFormat:NSLocalizedString(@"Are you sure you wish to discard the changes in this hunk?\n\nYou cannot undo this operation.", @"Asks whether the user really wants to discard a hunk in changes view after pressing the Discard Hunk button")]; + [alert beginSheetModalForWindow:[[controller view] window] + modalDelegate:self + didEndSelector:@selector(discardHunkAlertDidEnd:returnCode:contextInfo:) + contextInfo:(__bridge_retained void*)hunk]; + } else { + [self discardHunk:hunk]; + } +} + +- (void) setStateMessage:(NSString *)state +{ + id script = self.view.windowScriptObject; + [script callWebScriptMethod:@"setState" withArguments: [NSArray arrayWithObject:state]]; +} + +-(void)copy: (NSString *)text{ + NSArray *lines = [text componentsSeparatedByString:@"\n"]; + NSMutableArray *processedLines = [NSMutableArray arrayWithCapacity:lines.count -1]; + for (int i = 0; i < lines.count; i++) { + NSString *line = [lines objectAtIndex:i]; + if (line.length>0) { + [processedLines addObject:[line substringFromIndex:1]]; + } else { + [processedLines addObject:line]; + } + } + NSString *result = [processedLines componentsJoinedByString:@"\n"]; + [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; + [[NSPasteboard generalPasteboard] setString:result forType:NSStringPboardType]; +} + +- (BOOL)webView:(WebView *)webView + validateUserInterfaceItem:(id)item + defaultValidation:(BOOL)defaultValidation +{ + if (item.action == @selector(copy:)) { + return YES; + } else { + return defaultValidation; + } +} + +- (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)selector +{ + if (selector == @selector(copy:)) { + [self.script callWebScriptMethod:@"copy" withArguments:@[]]; + return YES; + } else { + return NO; + } +} + +@end diff --git a/PBWebController.h b/Classes/Controllers/PBWebController.h similarity index 75% rename from PBWebController.h rename to Classes/Controllers/PBWebController.h index 2715e5eac..bbc662d99 100644 --- a/PBWebController.h +++ b/Classes/Controllers/PBWebController.h @@ -10,7 +10,6 @@ #import @interface PBWebController : NSObject { - IBOutlet WebView* view; NSString *startFile; BOOL finishedLoading; @@ -18,11 +17,12 @@ NSMapTable *callbacks; // For the repository access - IBOutlet id repository; + __weak IBOutlet id repository; } -@property (retain) NSString *startFile; -@property (retain) id repository; +@property (weak) IBOutlet WebView *view; +@property NSString *startFile; +@property (weak) id repository; - (WebScriptObject *) script; - (void) closeView; diff --git a/PBWebController.m b/Classes/Controllers/PBWebController.m similarity index 61% rename from PBWebController.m rename to Classes/Controllers/PBWebController.m index d185593c7..c1a306727 100644 --- a/PBWebController.m +++ b/Classes/Controllers/PBWebController.m @@ -8,12 +8,17 @@ #import "PBWebController.h" #import "PBGitRepository.h" +#import "PBGitRepository_PBGitBinarySupport.h" #import "PBGitXProtocol.h" #import "PBGitDefaults.h" #include -@interface PBWebController() +static BOOL isDarkMode() { + return [[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"] isEqualToString:@"Dark"]; +} + +@interface PBWebController () - (void)preferencesChangedWithNotification:(NSNotification *)theNotification; @end @@ -35,29 +40,38 @@ - (void) awakeFromNib object:nil]; finishedLoading = NO; - [view setUIDelegate:self]; - [view setFrameLoadDelegate:self]; - [view setResourceLoadDelegate:self]; - [[view mainFrame] loadRequest:request]; + [self.view setUIDelegate:self]; + [self.view setFrameLoadDelegate:self]; + [self.view setResourceLoadDelegate:self]; + [self.view.preferences setDefaultFontSize:(int)[NSFont systemFontSize]]; + [self.view.mainFrame loadRequest:request]; + + // mainly for dark mode so it doesn't look white in bounce region + //FIXME ideally would be black though! + self.view.drawsBackground = false; } - (WebScriptObject *) script { - return [view windowScriptObject]; + return self.view.windowScriptObject; } -- (void) closeView +- (void)closeView { - if (view) - [view close]; + if (self.view) { + [[self script] setValue:nil forKey:@"Controller"]; + [self.view close]; + } + + [[NSNotificationCenter defaultCenter] removeObserver:self]; } # pragma mark Delegate methods - (void)webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)windowObject forFrame:(WebFrame *)frame { - id script = [view windowScriptObject]; - [script setValue: self forKey:@"Controller"]; + id script = self.view.windowScriptObject; + [script setValue:self forKey:@"Controller"]; } - (void) webView:(id) v didFinishLoadForFrame:(id) frame @@ -81,8 +95,7 @@ - (NSURLRequest *)webView:(WebView *)sender if (!self.repository) return request; - // TODO: Change this to canInitWithRequest - if ([[[request URL] scheme] isEqualToString:@"GitX"]) { + if ([PBGitXProtocol canInitWithRequest:request]) { NSMutableURLRequest *newRequest = [request mutableCopy]; [newRequest setRepository:self.repository]; return newRequest; @@ -91,6 +104,30 @@ - (NSURLRequest *)webView:(WebView *)sender return request; } +- (void)webView:(WebView *)sender +decidePolicyForNavigationAction:(NSDictionary *)actionInformation + request:(NSURLRequest *)request + frame:(WebFrame *)frame +decisionListener:(id )listener +{ + NSString* scheme = [[request URL] scheme]; + if ([scheme compare:@"http"] == NSOrderedSame || + [scheme compare:@"https"] == NSOrderedSame) + { + [listener ignore]; + [[NSWorkspace sharedWorkspace] openURL:[request URL]]; + } + else + { + [listener use]; + } +} + +- (NSUInteger)webView:(WebView *)webView +dragDestinationActionMaskForDraggingInfo:(id)draggingInfo +{ + return NSDragOperationNone; +} + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { @@ -110,8 +147,14 @@ - (void) log: (NSString*) logMessage - (BOOL) isReachable:(NSString *)hostname { - SCNetworkConnectionFlags flags; - if (!SCNetworkCheckReachabilityByName([hostname cStringUsingEncoding:NSASCIIStringEncoding], &flags)) + SCNetworkReachabilityRef target; + SCNetworkConnectionFlags flags = 0; + Boolean reachable; + target = SCNetworkReachabilityCreateWithName(NULL, [hostname cStringUsingEncoding:NSASCIIStringEncoding]); + reachable = SCNetworkReachabilityGetFlags(target, &flags); + CFRelease(target); + + if (!reachable) return FALSE; // If a connection is required, then it's not reachable @@ -146,51 +189,15 @@ - (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository * for (i = 0; i < length; i++) [realArguments addObject:[arguments webScriptValueAtIndex:i]]; - NSFileHandle *handle = [repo handleInWorkDirForArguments:realArguments]; - [callbacks setObject:callBack forKey:handle]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(JSRunCommandDone:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - [handle readToEndOfFileInBackgroundAndNotify]; -} - -- (void) callSelector:(NSString *)selectorString onObject:(id)object callBack:(WebScriptObject *)callBack -{ - NSArray *arguments = [NSArray arrayWithObjects:selectorString, object, nil]; - NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runInThread:) object:arguments]; - [callbacks setObject:callBack forKey:thread]; - [thread start]; -} - -- (void) runInThread:(NSArray *)arguments -{ - SEL selector = NSSelectorFromString([arguments objectAtIndex:0]); - id object = [arguments objectAtIndex:1]; - id ret = [object performSelector:selector]; - NSArray *returnArray = [NSArray arrayWithObjects:[NSThread currentThread], ret, nil]; - [self performSelectorOnMainThread:@selector(threadFinished:) withObject:returnArray waitUntilDone:NO]; -} - - -- (void) returnCallBackForObject:(id)object withData:(id)data -{ - WebScriptObject *a = [callbacks objectForKey: object]; - if (!a) { - NSLog(@"Could not find a callback for object: %@", object); - return; - } - - [callbacks removeObjectForKey:object]; - [a callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:@"", data, nil]]; -} - -- (void) threadFinished:(NSArray *)arguments -{ - [self returnCallBackForObject:[arguments objectAtIndex:0] withData:[arguments objectAtIndex:1]]; -} - -- (void) JSRunCommandDone:(NSNotification *)notification -{ - NSString *data = [[NSString alloc] initWithData:[[notification userInfo] valueForKey:NSFileHandleNotificationDataItem] encoding:NSUTF8StringEncoding]; - [self returnCallBackForObject:[notification object] withData:data]; + PBTask *task = [repo taskWithArguments:realArguments]; + [task performTaskWithCompletionHandler:^(NSData * _Nullable readData, NSError * _Nullable error) { + if (error) { + /* FIXME: Might want to inform the JS that something went wrong */ + NSLog(@"error: %@", error); + return; + } + [callBack callWebScriptMethod:@"call" withArguments:@[@"", readData]]; + }]; } - (void) preferencesChanged @@ -202,4 +209,9 @@ - (void)preferencesChangedWithNotification:(NSNotification *)theNotification [self preferencesChanged]; } +- (void)makeWebViewFirstResponder +{ + [self.view.window makeFirstResponder:self.view]; +} + @end diff --git a/PBWebDiffController.h b/Classes/Controllers/PBWebDiffController.h similarity index 100% rename from PBWebDiffController.h rename to Classes/Controllers/PBWebDiffController.h diff --git a/PBWebDiffController.m b/Classes/Controllers/PBWebDiffController.m similarity index 62% rename from PBWebDiffController.m rename to Classes/Controllers/PBWebDiffController.m index a537bd017..581c32eec 100644 --- a/PBWebDiffController.m +++ b/Classes/Controllers/PBWebDiffController.m @@ -18,6 +18,14 @@ - (void) awakeFromNib [diffController addObserver:self forKeyPath:@"diff" options:0 context:@"ChangedDiff"]; } +- (void)closeView +{ + [diffController removeObserver:self forKeyPath:@"diff"]; + + [super closeView]; +} + + - (void) didLoad { [self showDiff:diffController.diff]; @@ -25,7 +33,7 @@ - (void) didLoad - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([(NSString *)context isEqualToString: @"ChangedDiff"]) + if ([(__bridge NSString *)context isEqualToString: @"ChangedDiff"]) [self showDiff:diffController.diff]; } @@ -34,8 +42,11 @@ - (void) showDiff: (NSString *) diff if (diff == nil || !finishedLoading) return; - id script = [view windowScriptObject]; - [script callWebScriptMethod:@"showDiff" withArguments: [NSArray arrayWithObject:diff]]; + id script = self.view.windowScriptObject; + if ([diff length] == 0) + [script callWebScriptMethod:@"setMessage" withArguments:[NSArray arrayWithObject:@"There are no differences"]]; + else + [script callWebScriptMethod:@"showDiff" withArguments:[NSArray arrayWithObject:diff]]; } @end diff --git a/PBWebHistoryController.h b/Classes/Controllers/PBWebHistoryController.h similarity index 70% rename from PBWebHistoryController.h rename to Classes/Controllers/PBWebHistoryController.h index 749725a43..9972f371b 100644 --- a/PBWebHistoryController.h +++ b/Classes/Controllers/PBWebHistoryController.h @@ -13,16 +13,20 @@ #import "PBGitHistoryController.h" #import "PBRefContextDelegate.h" + +@class GTOID; + + @interface PBWebHistoryController : PBWebController { - IBOutlet PBGitHistoryController* historyController; - IBOutlet id contextMenuDelegate; + __weak IBOutlet PBGitHistoryController* historyController; + __weak IBOutlet id contextMenuDelegate; - NSString* currentSha; + GTOID *currentOID; NSString* diff; } -- (void) changeContentTo: (PBGitCommit *) content; - (void) sendKey: (NSString*) key; @property (readonly) NSString* diff; + @end diff --git a/Classes/Controllers/PBWebHistoryController.m b/Classes/Controllers/PBWebHistoryController.m new file mode 100644 index 000000000..63765425c --- /dev/null +++ b/Classes/Controllers/PBWebHistoryController.m @@ -0,0 +1,337 @@ +// +// PBWebGitController.m +// GitTest +// +// Created by Pieter de Bie on 14-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBWebHistoryController.h" +#import "PBGitDefaults.h" +#import +#import "PBGitRef.h" +#import "PBGitRevSpecifier.h" +#import + +@interface PBWebHistoryController () +@property (nonatomic) atomic_ulong commitSummaryGeneration; +@end + +@implementation PBWebHistoryController + +@synthesize diff; + +- (void) awakeFromNib +{ + startFile = @"history"; + repository = historyController.repository; + [super awakeFromNib]; + [historyController addObserver:self forKeyPath:@"webCommits" options:0 context:@"ChangedCommit"]; +} + +- (void)closeView +{ + [[self script] setValue:nil forKey:@"commit"]; + [historyController removeObserver:self forKeyPath:@"webCommits"]; + + [super closeView]; +} + +- (void) didLoad +{ + currentOID = nil; + [self changeContentTo:historyController.webCommits]; +} + +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([(__bridge NSString *)context isEqualToString: @"ChangedCommit"]) + [self changeContentTo:historyController.webCommits]; + else + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +- (void) changeContentTo:(NSArray *)commits +{ + if (commits == nil || commits.count == 0 || !finishedLoading) { + return; + } + + if (commits.count == 1) { + [self changeContentToCommit:commits.firstObject]; + } + else { + [self changeContentToMultipleSelectionMessage]; + } +} + +- (void) changeContentToMultipleSelectionMessage { + NSArray *arguments = @[ + @[NSLocalizedString(@"Multiple commits are selected.", @"Multiple selection Message: Title"), + NSLocalizedString(@"Use the Copy command to copy their information.", @"Multiple selection Message: Copy Command"), + NSLocalizedString(@"Or select a single commit to see its diff.", @"Multiple selection Message: Diff Hint") + ]]; + [[self script] callWebScriptMethod:@"showMultipleSelectionMessage" withArguments:arguments]; +} + +static NSString *deltaTypeName(GTDeltaType t) { + switch (t) { + case GTDeltaTypeUnmodified: return @"unmodified"; + case GTDeltaTypeAdded: return @"added"; + case GTDeltaTypeDeleted: return @"removed"; + case GTDeltaTypeModified: return @"modified"; + case GTDeltaTypeRenamed: return @"renamed"; + case GTDeltaTypeCopied: return @"copied"; + case GTDeltaTypeIgnored: return @"ignored"; + case GTDeltaTypeUntracked: return @"untracked"; + case GTDeltaTypeTypeChange: return @"type changed"; + case GTDeltaTypeUnreadable: return @"unreadable"; + case GTDeltaTypeConflicted: return @"conflicted"; + } +} + +static NSDictionary *loadCommitSummary(GTRepository *repo, GTCommit *commit, BOOL (^isCanceled)(void)); + +// A GTDiffDelta's GTDiffFile does not always set the file size. See `git_diff_get_delta`. +static NSUInteger reallyGetFileSize(GTRepository *repo, GTDiffFile *file) { + GTObjectDatabase *odb = [repo objectDatabaseWithError:nil]; + if (!odb) return 0; + size_t size = 0; + git_otype otype; + if (git_odb_read_header(&size, &otype, odb.git_odb, file.OID.git_oid) != 0) { + return 0; + } + return size; +} + +- (void) changeContentToCommit:(PBGitCommit *)commit +{ + // The sha is the same, but refs may have changed. reload it lazy + if ([currentOID isEqual:commit.OID]) + { + [[self script] callWebScriptMethod:@"reload" withArguments: nil]; + return; + } + + NSArray *arguments = @[commit, [[[historyController repository] headRef] simpleRef]]; + id scriptResult = [[self script] callWebScriptMethod:@"loadCommit" withArguments: arguments]; + if (!scriptResult) { + // the web view is not really ready for scripting??? + [self performSelector:_cmd withObject:commit afterDelay:0.05]; + return; + } + currentOID = commit.OID; + + unsigned long gen = atomic_fetch_add(&_commitSummaryGeneration, 1) + 1; + + // Open a new repo instance for the background queue + NSError *err = nil; + GTRepository *repo = + [GTRepository repositoryWithURL:[repository gtRepo].gitDirectoryURL error:&err]; + if (!repo) { + NSLog(@"Failed to open repository: %@", err); + return; + } + GTCommit *queueCommit = [repo lookUpObjectByOID:commit.OID error:&err]; + if (!queueCommit) { + NSLog(@"Failed to find commit: %@", err); + return; + } + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + NSDictionary *summary = loadCommitSummary(repo, queueCommit, ^BOOL { + return gen != atomic_load(&self->_commitSummaryGeneration); + }); + if (!summary) return; + NSError *err = nil; + NSString *summaryJSON = + [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:summary + options:0 + error:&err] + encoding:NSUTF8StringEncoding]; + if (!summaryJSON) { + NSLog(@"Commit summary JSON error: %@", err); + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self commitSummaryLoaded:summaryJSON forOID:commit.OID]; + }); + }); +} + +static NSDictionary *loadCommitSummary(GTRepository *repo, GTCommit *commit, BOOL (^isCanceled)(void)) { + if (isCanceled()) return nil; + GTDiffFindOptionsFlags flags = GTDiffFindOptionsFlagsFindRenames; + if (![PBGitDefaults showWhitespaceDifferences]) { + flags |= GTDiffFindOptionsFlagsIgnoreWhitespace; + } + NSError *err = nil; + GTDiff *d = [GTDiff diffOldTree:commit.parents.firstObject.tree + withNewTree:commit.tree + inRepository:repo + options:@{} + error:&err]; + + if (!d) { + NSLog(@"Commit summary diff error: %@", err); + return nil; + } + + // Rewrite the diff to display moved files. + [d findSimilarWithOptions:@{GTDiffFindOptionsFlagsKey:@(flags)}]; + + if (isCanceled()) return nil; + NSMutableArray *> *fileDeltas = [NSMutableArray array]; + NSMutableString *fullDiff = [NSMutableString string]; + [d enumerateDeltasUsingBlock:^(GTDiffDelta *_Nonnull delta, BOOL *_Nonnull stop) { + if (isCanceled()) { + *stop = YES; + return; + } + NSUInteger numLinesAdded = 0; + NSUInteger numLinesRemoved = 0; + NSError *err = nil; + GTDiffPatch *patch = [delta generatePatch:&err]; + if (isCanceled()) { + *stop = YES; + return; + } + if (patch) { + numLinesAdded = patch.addedLinesCount; + numLinesRemoved = patch.deletedLinesCount; + NSData *patchData = patch.patchData; + if (patchData) { + NSString *patchString = + [[NSString alloc] initWithData:patchData + encoding:NSUTF8StringEncoding]; + if (!patchString) { + patchString = + [[NSString alloc] initWithData:patchData + encoding:NSISOLatin1StringEncoding]; + } + if (patchString) { + [fullDiff appendString:patchString]; + } + } + // Use the patch's delta as it may have loaded more file sizes. + delta = patch.delta; + } else { + NSLog(@"generatePatch error: %@", err); + } + GTDiffFile *oldFile = delta.oldFile; + GTDiffFile *newFile = delta.newFile; + NSUInteger oldFileSize = oldFile.size; + NSUInteger newFileSize = newFile.size; + if (oldFileSize == 0 && (oldFile.flags & GIT_DIFF_FLAG_EXISTS)) { + oldFileSize = reallyGetFileSize(repo, newFile); + } + if (newFileSize == 0 && (newFile.flags & GIT_DIFF_FLAG_EXISTS)) { + newFileSize = reallyGetFileSize(repo, newFile); + } + [fileDeltas addObject:@{ + @"filename" : newFile.path, + @"oldFilename" : oldFile.path, + @"newFilename" : newFile.path, + @"changeType" : deltaTypeName(delta.type), + @"oldFileSize" : @(oldFileSize), + @"newFileSize" : @(newFileSize), + @"numLinesAdded" : @(numLinesAdded), + @"numLinesRemoved" : @(numLinesRemoved), + @"binary" : [NSNumber numberWithBool:(delta.flags & GTDiffFileFlagBinary) != 0], + }]; + }]; + if (isCanceled()) return nil; + return @{ + @"filesInfo" : fileDeltas, + @"fullDiff" : fullDiff, + }; +} + +- (void)commitSummaryLoaded:(NSString *)summaryJSON forOID:(GTOID *)summaryOID +{ + if (![currentOID isEqual:summaryOID]) { + // a different summary finished loading late + return; + } + + [self.view.windowScriptObject callWebScriptMethod:@"loadCommitDiff" withArguments:@[summaryJSON]]; +} + +- (void)selectCommit:(NSString *)sha +{ + [historyController selectCommit: [GTOID oidWithSHA: sha]]; +} + +- (void) sendKey: (NSString*) key +{ + id script = self.view.windowScriptObject; + [script callWebScriptMethod:@"handleKeyFromCocoa" withArguments: [NSArray arrayWithObject:key]]; +} + +- (void) copySource +{ + NSString *source = [(DOMHTMLElement *)self.view.mainFrame.DOMDocument.documentElement outerHTML]; + NSPasteboard *a =[NSPasteboard generalPasteboard]; + [a declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; + [a setString:source forType: NSStringPboardType]; +} + +- (NSArray *) webView:(WebView *)sender +contextMenuItemsForElement:(NSDictionary *)element + defaultMenuItems:(NSArray *)defaultMenuItems +{ + DOMNode *node = [element valueForKey:@"WebElementDOMNode"]; + + while (node) { + // Every ref has a class name of 'refs' and some other class. We check on that to see if we pressed on a ref. + if ([[node className] hasPrefix:@"refs "]) { + NSString *selectedRefString = [[[node childNodes] item:0] textContent]; + for (PBGitRef *ref in historyController.webCommits.firstObject.refs) { + if ([[ref shortName] isEqualToString:selectedRefString]) + return [contextMenuDelegate menuItemsForRef:ref]; + } + NSLog(@"Could not find selected ref!"); + return defaultMenuItems; + } + if ([node hasAttributes] && [[node attributes] getNamedItem:@"representedFile"]) + return [historyController menuItemsForPaths:[NSArray arrayWithObject:[[[node attributes] getNamedItem:@"representedFile"] nodeValue]]]; + else if ([[node class] isEqual:[DOMHTMLImageElement class]]) { + // Copy Image is the only menu item that makes sense here since we don't need + // to download the image or open it in a new window (besides with the + // current implementation these two entries can crash GitX anyway) + for (NSMenuItem *item in defaultMenuItems) + if ([item tag] == WebMenuItemTagCopyImageToClipboard) + return [NSArray arrayWithObject:item]; + return nil; + } + + node = [node parentNode]; + } + + return defaultMenuItems; +} + + +// Open external links in the default browser +- (void)webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)actionInformation + request:(NSURLRequest *)request + newFrameName:(NSString *)frameName + decisionListener:(id < WebPolicyDecisionListener >)listener +{ + [[NSWorkspace sharedWorkspace] openURL:[request URL]]; +} + +- getConfig:(NSString *)key +{ + NSError *error = nil; + GTConfiguration* config = [historyController.repository.gtRepo configurationWithError:&error]; + return [config stringForKey:key]; +} + + +- (void) preferencesChanged +{ + [[self script] callWebScriptMethod:@"enableFeatures" withArguments:nil]; +} + +@end diff --git a/Classes/GitXRelativeDateFormatter.h b/Classes/GitXRelativeDateFormatter.h new file mode 100644 index 000000000..b5f1244d3 --- /dev/null +++ b/Classes/GitXRelativeDateFormatter.h @@ -0,0 +1,16 @@ +// +// GitXRelativeDateFormatter.h +// GitX +// +// Created by Nathan Kinsinger on 9/1/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@interface GitXRelativeDateFormatter : NSFormatter { + +} + +@end diff --git a/Classes/GitXRelativeDateFormatter.m b/Classes/GitXRelativeDateFormatter.m new file mode 100644 index 000000000..e85f8d560 --- /dev/null +++ b/Classes/GitXRelativeDateFormatter.m @@ -0,0 +1,80 @@ +// +// GitXRelativeDateFormatter.m +// GitX +// +// Created by Nathan Kinsinger on 9/1/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "GitXRelativeDateFormatter.h" + + +#define MINUTE 60 +#define HOUR (60 * MINUTE) + +#define WEEK 7 + + +@implementation GitXRelativeDateFormatter + +- (NSString *)stringForObjectValue:(id)date +{ + if (![date isKindOfClass:[NSDate class]]) + return nil; + + NSDate *now = [NSDate date]; + + NSInteger secondsAgo = lround([now timeIntervalSinceDate:date]); + + if (secondsAgo < 0) + return @"Future"; + + if (secondsAgo < (2 * MINUTE)) + return @"1 mn"; + + if (secondsAgo < HOUR) + return [NSString stringWithFormat:@"%ld mns", (secondsAgo / MINUTE)]; + + if (secondsAgo < (2 * HOUR)) + return @"1 hr"; + + // figure out # of days ago based on calender days (so yesterday is the day before today not 24 hours ago) + NSDateFormatter *midnightFormmatter = [[NSDateFormatter alloc] init]; + [midnightFormmatter setDateFormat:@"yyyy-MM-dd"]; + NSDate *midnightOnTargetDate = [midnightFormmatter dateFromString:[midnightFormmatter stringFromDate:date]]; + NSDate *midnightToday = [midnightFormmatter dateFromString:[midnightFormmatter stringFromDate:now]]; + + // use NSCalendar so it will handle things like leap years correctly + NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) + fromDate:midnightOnTargetDate + toDate:midnightToday + options:0]; + NSInteger yearsAgo = [components year]; + NSInteger monthsAgo = [components month]; + NSInteger daysAgo = [components day]; + + if (yearsAgo < 2) { + if (monthsAgo == 0) { + // return "hours ago" if it's still today, but "Yesterday" only if more than 6 hours ago + // gives people a little time to get used to the idea that yesterday is over :) + if ((daysAgo == 0) || (secondsAgo < (6 * HOUR))) + return [NSString stringWithFormat:@"%ld hrs", (secondsAgo / HOUR)]; + if (daysAgo == 1) + return @"1 dy"; + + if (daysAgo >= (2 * WEEK)) + return [NSString stringWithFormat:@"%ld wks", (daysAgo / WEEK)]; + + return [NSString stringWithFormat:@"%ld dys", daysAgo]; + } + + if (monthsAgo == 1) + return @"1 mth"; + + return [NSString stringWithFormat:@"%ld mths", monthsAgo]; + } + + return [NSString stringWithFormat:@"%ld yrs", yearsAgo]; +} + +@end diff --git a/Classes/GitXScriptingConstants.h b/Classes/GitXScriptingConstants.h new file mode 100644 index 000000000..306b5b53a --- /dev/null +++ b/Classes/GitXScriptingConstants.h @@ -0,0 +1,18 @@ +// +// GitXScriptingConstants.h +// GitX +// +// Created by Nathan Kinsinger on 8/15/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#define kGitXBundleIdentifier @"com.codebasesaga.macOS.GitX" + + +#define kGitXAEKeyArgumentsList 'ARGS' + +#define kGitXCloneDestinationURLKey @"destinationURL" +#define kGitXCloneIsBareKey @"isBare" + +#define kGitXFindSearchStringKey @"searchString" +#define kGitXFindInModeKey @"inMode" diff --git a/Classes/NSColor+RGB.h b/Classes/NSColor+RGB.h new file mode 100644 index 000000000..431a7167f --- /dev/null +++ b/Classes/NSColor+RGB.h @@ -0,0 +1,15 @@ +// +// NSColor+RGB.h +// GitX +// +// Created by Rowan James on 18/08/13. +// +// + +#import + +@interface NSColor (RGB) + ++ (NSColor *)colorWithR:(uint8_t)r G:(uint8_t)g B:(uint8_t)b; + +@end diff --git a/Classes/NSColor+RGB.m b/Classes/NSColor+RGB.m new file mode 100644 index 000000000..0e710aaff --- /dev/null +++ b/Classes/NSColor+RGB.m @@ -0,0 +1,20 @@ +// +// NSColor+RGB.m +// GitX +// +// Created by Rowan James on 18/08/13. +// +// + +#import "NSColor+RGB.h" + +@implementation NSColor (RGB) + ++ (NSColor *)colorWithR:(uint8_t)r G:(uint8_t)g B:(uint8_t)b +{ + const CGFloat MAX_RGB = 255.0; + NSColor *result = [NSColor colorWithCalibratedRed:(r/MAX_RGB) green:(g/MAX_RGB) blue:(b/MAX_RGB) alpha:1]; + return result; +} + +@end diff --git a/Classes/NSSplitView+GitX.h b/Classes/NSSplitView+GitX.h new file mode 100644 index 000000000..556bf9246 --- /dev/null +++ b/Classes/NSSplitView+GitX.h @@ -0,0 +1,15 @@ +// +// NSSplitView+GitX.h +// GitX +// +// Created by Kent Sutherland on 4/25/17. +// +// + +#import + +@interface NSSplitView (GitX) + +- (void)pb_restoreAutosavedPositions; + +@end diff --git a/Classes/NSSplitView+GitX.m b/Classes/NSSplitView+GitX.m new file mode 100644 index 000000000..ddeb88b86 --- /dev/null +++ b/Classes/NSSplitView+GitX.m @@ -0,0 +1,47 @@ +// +// NSSplitView+GitX.m +// GitX +// +// Created by Kent Sutherland on 4/25/17. +// +// + +#import "NSSplitView+GitX.h" + +@implementation NSSplitView (GitX) + +// Taken from http://stackoverflow.com/questions/16587058/nssplitview-auto-saving-divider-positions-doesnt-work-with-auto-layout-enable +// Without this split views don't automatically restore for windows that aren't reopened by state restoration +- (void)pb_restoreAutosavedPositions +{ + NSString *key = [NSString stringWithFormat:@"NSSplitView Subview Frames %@", self.autosaveName]; + NSArray *subviewFrames = [[NSUserDefaults standardUserDefaults] objectForKey:key]; + + // the last frame is skipped because I have one less divider than I have frames + for (NSInteger i = 0; i < subviewFrames.count; i++) { + if (i < self.subviews.count) { // safety-check (in case number of views have been removed while dev) + // this is the saved frame data - it's an NSString + NSString *frameString = subviewFrames[i]; + NSArray *components = [frameString componentsSeparatedByString:@", "]; + + // Manage the 'hidden state' per view + BOOL hidden = [components[4] boolValue]; + NSView *subview = [self subviews][i]; + + [subview setHidden:hidden]; + + // Set height (horizontal) or width (vertical) + if(![self isVertical]) { + CGFloat height = [components[3] floatValue]; + + [subview setFrameSize:NSMakeSize(subview.frame.size.width, height)]; + } else { + CGFloat width = [components[2] floatValue]; + + [subview setFrameSize:NSMakeSize(width, subview.frame.size.height)]; + } + } + } +} + +@end diff --git a/PBChangedFile.h b/Classes/PBChangedFile.h similarity index 96% rename from PBChangedFile.h rename to Classes/PBChangedFile.h index bffc11073..e567e8965 100644 --- a/PBChangedFile.h +++ b/Classes/PBChangedFile.h @@ -7,7 +7,6 @@ // #import -#import "PBGitRepository.h" typedef enum { NEW, diff --git a/PBChangedFile.m b/Classes/PBChangedFile.m similarity index 84% rename from PBChangedFile.m rename to Classes/PBChangedFile.m index ac9b4d3fb..0802b351e 100644 --- a/PBChangedFile.m +++ b/Classes/PBChangedFile.m @@ -7,7 +7,6 @@ // #import "PBChangedFile.h" -#import "PBEasyPipe.h" @implementation PBChangedFile @@ -15,10 +14,11 @@ @implementation PBChangedFile - (id) initWithPath:(NSString *)p { - if (![super init]) - return nil; - - path = p; + self = [super init]; + + if (self) { + path = p; + } return self; } @@ -45,8 +45,7 @@ - (NSImage *) icon filename = @"empty_file"; break; } - NSString *p = [[NSBundle mainBundle] pathForResource:filename ofType:@"png"]; - return [[NSImage alloc] initByReferencingFile: p]; + return [NSImage imageNamed:filename]; } + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector diff --git a/Classes/PBCommitList.h b/Classes/PBCommitList.h new file mode 100644 index 000000000..8d748fca9 --- /dev/null +++ b/Classes/PBCommitList.h @@ -0,0 +1,32 @@ +// +// PBCommitList.h +// GitX +// +// Created by Pieter de Bie on 9/11/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import + +@class PBGitHistoryController; +@class PBWebHistoryController; +@class PBHistorySearchController; + +typedef void(^PBFindPanelActionBlock)(id sender); + +@interface PBCommitList : NSTableView { + __weak IBOutlet WebView* webView; + __weak IBOutlet PBWebHistoryController *webController; + __weak IBOutlet PBGitHistoryController *controller; + __weak IBOutlet PBHistorySearchController *searchController; + + BOOL useAdjustScroll; + NSPoint mouseDownPoint; +} + +@property (readonly) NSPoint mouseDownPoint; +@property (assign) BOOL useAdjustScroll; +@property (copy) PBFindPanelActionBlock findPanelActionBlock; + +@end diff --git a/Classes/PBCommitList.m b/Classes/PBCommitList.m new file mode 100644 index 000000000..4ab0529df --- /dev/null +++ b/Classes/PBCommitList.m @@ -0,0 +1,194 @@ +// +// PBCommitList.m +// GitX +// +// Created by Pieter de Bie on 9/11/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBCommitList.h" +#import "PBGitRevisionCell.h" +#import "PBWebHistoryController.h" +#import "PBHistorySearchController.h" + +@interface PBCommitList () +@end + +@implementation PBCommitList + +@synthesize mouseDownPoint; +@synthesize useAdjustScroll; + +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { + return NSDragOperationCopy; +} + +- (void)keyDown:(NSEvent *)event +{ + NSString* character = [event charactersIgnoringModifiers]; + + // Pass on command-shift up/down to the responder. We want the splitview to capture this. + if ([event modifierFlags] & NSShiftKeyMask && [event modifierFlags] & NSCommandKeyMask && ([event keyCode] == 0x7E || [event keyCode] == 0x7D)) { + [self.nextResponder keyDown:event]; + return; + } + + if ([character isEqualToString:@" "]) { + if (controller.selectedCommitDetailsIndex == 0) { + if ([event modifierFlags] & NSShiftKeyMask) + [webView scrollPageUp:self]; + else + [webView scrollPageDown:self]; + } + else + [controller toggleQLPreviewPanel:self]; + } + else if ([character rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"jkcv"]].location == 0) + [webController sendKey: character]; + else + [super keyDown: event]; +} + +// !!! Andre Berg 20100330: Used from -scrollSelectionToTopOfViewFrom: of PBGitHistoryController +// so that when the history controller udpates the branch filter the origin of the superview gets +// shifted into multiples of the row height. Otherwise the top selected row will always be off by +// a little bit depending on how much the bottom half of the split view is dragged down. +- (NSRect)adjustScroll:(NSRect)proposedVisibleRect { + + //NSLog(@"[%@ %s]: proposedVisibleRect: %@", [self class], _cmd, NSStringFromRect(proposedVisibleRect)); + NSRect newRect = proposedVisibleRect; + + // !!! Andre Berg 20100330: only modify if -scrollSelectionToTopOfViewFrom: has set useAdjustScroll to YES + // Otherwise we'd also constrain things like middle mouse scrolling. + if (useAdjustScroll) { + NSInteger rh = (NSInteger)self.rowHeight; + NSInteger ny = (NSInteger)proposedVisibleRect.origin.y % (NSInteger)rh; + NSInteger adj = rh - ny; + // check the targeted row and see if we need to add or subtract the difference (if there is one)... + NSRect sr = [self rectOfRow:[self selectedRow]]; + // NSLog(@"[%@ %s]: selectedRow %d, rect: %@", [self class], _cmd, [self selectedRow], NSStringFromRect(sr)); + if (sr.origin.y > proposedVisibleRect.origin.y) { + // NSLog(@"[%@ %s] selectedRow.origin.y > proposedVisibleRect.origin.y. adding adj (%d)", [self class], _cmd, adj); + newRect = NSMakeRect(newRect.origin.x, newRect.origin.y + adj, newRect.size.width, newRect.size.height); + } else if (sr.origin.y < proposedVisibleRect.origin.y) { + // NSLog(@"[%@ %s] selectedRow.origin.y < proposedVisibleRect.origin.y. subtracting ny (%d)", [self class], _cmd, ny); + newRect = NSMakeRect(newRect.origin.x, newRect.origin.y - ny , newRect.size.width, newRect.size.height); + } else { + // NSLog(@"[%@ %s] selectedRow.origin.y == proposedVisibleRect.origin.y. leaving as is", [self class], _cmd); + } + } + //NSLog(@"[%@ %s]: newRect: %@", [self class], _cmd, NSStringFromRect(newRect)); + return newRect; +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + mouseDownPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; + [super mouseDown:theEvent]; +} + +- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows + tableColumns:(NSArray *)tableColumns + event:(NSEvent *)dragEvent + offset:(NSPointPointer)dragImageOffset +{ + NSPoint location = mouseDownPoint; + NSInteger row = [self rowAtPoint:location]; + NSInteger column = [self columnAtPoint:location]; + PBGitRevisionCell *cell = (PBGitRevisionCell *)[self preparedCellAtColumn:column row:row]; + NSRect cellFrame = [self frameOfCellAtColumn:column row:row]; + + int index = -1; + + if ([cell respondsToSelector:@selector(indexAtX:)]) { + index = [cell indexAtX:(location.x - cellFrame.origin.x)]; + } + + if (index == -1) + return [super dragImageForRowsWithIndexes:dragRows tableColumns:tableColumns event:dragEvent offset:dragImageOffset]; + + NSRect rect = [cell rectAtIndex:index]; + + NSImage *newImage = [[NSImage alloc] initWithSize:NSMakeSize(rect.size.width + 3, rect.size.height + 3)]; + rect.origin = NSMakePoint(0.5, 0.5); + + [newImage lockFocus]; + [cell drawLabelAtIndex:index inRect:rect]; + [newImage unlockFocus]; + + *dragImageOffset = NSMakePoint(rect.size.width / 2 + 10, 0); + return newImage; + +} + + +#pragma mark Row highlighting + +- (NSColor *)searchResultHighlightColorForRow:(NSInteger)rowIndex +{ + // if the row is selected use default colors + if ([self isRowSelected:rowIndex]) { + if ([[self window] isKeyWindow]) { + if ([[self window] firstResponder] == self) { + return [NSColor alternateSelectedControlColor]; + } + return [NSColor selectedControlColor]; + } + return [NSColor secondarySelectedControlColor]; + } + + // light blue color highlighting search results + return [NSColor colorWithCalibratedRed:0.751f green:0.831f blue:0.943f alpha:0.800f]; +} + +- (NSColor *)searchResultHighlightStrokeColorForRow:(NSInteger)rowIndex +{ + if ([self isRowSelected:rowIndex]) + return [NSColor colorWithCalibratedWhite:0.0f alpha:0.30f]; + + return [NSColor colorWithCalibratedWhite:0.0f alpha:0.05f]; +} + +- (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)tableViewClipRect +{ + NSRect rowRect = [self rectOfRow:rowIndex]; + BOOL isRowVisible = NSIntersectsRect(rowRect, tableViewClipRect); + + // draw special highlighting if the row is part of search results + if (isRowVisible && [searchController isRowInSearchResults:rowIndex]) { + NSRect highlightRect = NSInsetRect(rowRect, 1.0f, 1.0f); + CGFloat radius = highlightRect.size.height / 2.0f; + + NSBezierPath *highlightPath = [NSBezierPath bezierPathWithRoundedRect:highlightRect xRadius:radius yRadius:radius]; + + [[self searchResultHighlightColorForRow:rowIndex] set]; + [highlightPath fill]; + + [[self searchResultHighlightStrokeColorForRow:rowIndex] set]; + [highlightPath stroke]; + } + + // draws the content inside the row + [super drawRow:rowIndex clipRect:tableViewClipRect]; +} + +- (void)highlightSelectionInClipRect:(NSRect)tableViewClipRect +{ + // disable highlighting if the selected row is part of search results + // instead do the highlighting in drawRow:clipRect: above + if ([searchController isRowInSearchResults:[self selectedRow]]) + return; + + [super highlightSelectionInClipRect:tableViewClipRect]; +} + + +- (IBAction)performFindPanelAction:(id)sender +{ + PBFindPanelActionBlock block = self.findPanelActionBlock; + if (block) { + block(sender); + } +} + +@end diff --git a/PBGraphCellInfo.h b/Classes/PBGraphCellInfo.h similarity index 54% rename from PBGraphCellInfo.h rename to Classes/PBGraphCellInfo.h index 5948e0f83..1669e5cce 100644 --- a/PBGraphCellInfo.h +++ b/Classes/PBGraphCellInfo.h @@ -11,19 +11,20 @@ @interface PBGraphCellInfo : NSObject { - int position; struct PBGitGraphLine *lines; - int nLines; - int numColumns; + long nLines; + long position; + long numColumns; char sign; } -@property(readonly) struct PBGitGraphLine *lines; -@property(assign) int nLines; -@property(assign) int position, numColumns; +@property struct PBGitGraphLine *lines; +@property(assign) long nLines; +@property(assign) long position; +@property(assign) long numColumns; @property(assign) char sign; -- (id)initWithPosition:(int) p andLines:(struct PBGitGraphLine *) l; +- (id)initWithPosition:(long)p andLines:(struct PBGitGraphLine *)l; -@end \ No newline at end of file +@end diff --git a/Classes/PBGraphCellInfo.m b/Classes/PBGraphCellInfo.m new file mode 100644 index 000000000..e4b488b24 --- /dev/null +++ b/Classes/PBGraphCellInfo.m @@ -0,0 +1,53 @@ +// +// PBGraphCellInfo.m +// GitX +// +// Created by Pieter de Bie on 27-08-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGraphCellInfo.h" + + +@implementation PBGraphCellInfo +@synthesize nLines, position, numColumns, sign; + +- (id)initWithPosition:(long)p andLines:(struct PBGitGraphLine *)l +{ + position = p; + lines = l; + + return self; +} + +- (struct PBGitGraphLine*)lines +{ + return lines; +} + +- (void)setLines:(struct PBGitGraphLine *)l +{ + free(lines); + lines = l; +} + +- (NSString *)description { return [self debugDescription]; } + +- (NSString *)debugDescription +{ + NSMutableString *desc = [NSMutableString stringWithFormat:@"<%@: %p position: %ld numColumns: %ld nLines: %ld sign: '%c'>", + NSStringFromClass([self class]), self, position, numColumns, nLines, sign]; + for (int lineIndex = 0; lineIndex < nLines; lineIndex++) { + struct PBGitGraphLine line = lines[lineIndex]; + [desc appendString:[NSString stringWithFormat:@"\n\t", + line.upper, line.from, line.to, line.colorIndex]]; + } + return desc; +} + +-(void) dealloc +{ + free(lines); +} + +@end diff --git a/Classes/PBMacros.h b/Classes/PBMacros.h new file mode 100644 index 000000000..ea7220050 --- /dev/null +++ b/Classes/PBMacros.h @@ -0,0 +1,28 @@ +// +// PBMacros.h +// GitX +// +// Created by Etienne on 17/02/2017. +// +// + +#import + +#define GITX_DEPRECATED __attribute__ ((deprecated)) +#define GITX_DEPRECATED_MSG(x) __attribute__ ((deprecated(x))) + +#define PBLogFunction(x, ...) PBLogFunctionImpl(__FUNCTION__, x, ## __VA_ARGS__) +#define PBLogError(x) PBLogErrorImpl(__FUNCTION__, x) + + +#ifdef __cplusplus +extern "C" { +#endif + +void PBLogFunctionImpl(const char *function, NSString *format, ...); + +void PBLogErrorImpl(const char *function, NSError *error); + +#ifdef __cplusplus +} +#endif diff --git a/Classes/PBMacros.m b/Classes/PBMacros.m new file mode 100644 index 000000000..612da68d8 --- /dev/null +++ b/Classes/PBMacros.m @@ -0,0 +1,33 @@ +// +// PBMacros.m +// GitX +// +// Created by Etienne on 17/02/2017. +// +// + +#import "PBMacros.h" + +#include + +void PBLogFunctionImpl(const char *function, NSString *format, ...) { + va_list arg; + + if (!format) { + NSLog(@"%s", function); + return; + } + + va_start(arg, format); + + NSString *log = [[NSString alloc] initWithFormat:format arguments:arg]; + + va_end(arg); + + NSLog(@"%s: %@", function, log); +} + +void PBLogErrorImpl(const char *function, NSError *error) { + if (!error) return; + NSLog(@"%s: %@", function, error); +} diff --git a/PBNSURLPathUserDefaultsTransfomer.h b/Classes/PBNSURLPathUserDefaultsTransfomer.h similarity index 100% rename from PBNSURLPathUserDefaultsTransfomer.h rename to Classes/PBNSURLPathUserDefaultsTransfomer.h diff --git a/PBNSURLPathUserDefaultsTransfomer.m b/Classes/PBNSURLPathUserDefaultsTransfomer.m similarity index 100% rename from PBNSURLPathUserDefaultsTransfomer.m rename to Classes/PBNSURLPathUserDefaultsTransfomer.m diff --git a/Classes/PBRefContextDelegate.h b/Classes/PBRefContextDelegate.h new file mode 100644 index 000000000..7e4454383 --- /dev/null +++ b/Classes/PBRefContextDelegate.h @@ -0,0 +1,18 @@ +// +// PBRefContextDelegate.h +// GitX +// +// Created by Pieter de Bie on 01-11-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + + +@class PBGitRef; +@class PBGitCommit; + + +@protocol PBRefContextDelegate +- (NSArray *) menuItemsForRef:(PBGitRef *)ref; +- (NSArray *) menuItemsForCommits:(NSArray *)commits; +- (NSArray *) menuItemsForRow:(NSInteger)rowIndex; +@end diff --git a/PBUnsortableTableHeader.h b/Classes/PBUnsortableTableHeader.h similarity index 92% rename from PBUnsortableTableHeader.h rename to Classes/PBUnsortableTableHeader.h index 2881e9368..2b568270b 100644 --- a/PBUnsortableTableHeader.h +++ b/Classes/PBUnsortableTableHeader.h @@ -12,7 +12,7 @@ @interface PBUnsortableTableHeader : NSTableHeaderView { IBOutlet NSArrayController *controller; int clickCount; - int columnIndex; + NSInteger columnIndex; } @end diff --git a/PBUnsortableTableHeader.m b/Classes/PBUnsortableTableHeader.m similarity index 93% rename from PBUnsortableTableHeader.m rename to Classes/PBUnsortableTableHeader.m index 9afb8eb78..9b0b64a35 100644 --- a/PBUnsortableTableHeader.m +++ b/Classes/PBUnsortableTableHeader.m @@ -14,7 +14,7 @@ @implementation PBUnsortableTableHeader - (void)mouseDown:(NSEvent *)theEvent { NSPoint location = [self convertPoint:[[self window] mouseLocationOutsideOfEventStream] fromView:[[self window] contentView]]; - int aColumnIndex = [self columnAtPoint:location]; + NSInteger aColumnIndex = [self columnAtPoint:location]; // If the user pressed on another column, reset if (aColumnIndex != columnIndex) diff --git a/Classes/Terminal.h b/Classes/Terminal.h new file mode 100644 index 000000000..b4052ada3 --- /dev/null +++ b/Classes/Terminal.h @@ -0,0 +1,153 @@ +/* + * Terminal.h + */ + +#import +#import + + +@class TerminalApplication, TerminalWindow, TerminalSettingsSet, TerminalTab; + +enum TerminalSaveOptions { + TerminalSaveOptionsYes = 'yes ' /* Save the file. */, + TerminalSaveOptionsNo = 'no ' /* Do not save the file. */, + TerminalSaveOptionsAsk = 'ask ' /* Ask the user whether or not to save the file. */ +}; +typedef enum TerminalSaveOptions TerminalSaveOptions; + +enum TerminalPrintingErrorHandling { + TerminalPrintingErrorHandlingStandard = 'lwst' /* Standard PostScript error handling */, + TerminalPrintingErrorHandlingDetailed = 'lwdt' /* print a detailed report of PostScript errors */ +}; +typedef enum TerminalPrintingErrorHandling TerminalPrintingErrorHandling; + +@protocol TerminalGenericMethods + +- (void) closeSaving:(TerminalSaveOptions)saving savingIn:(NSURL *)savingIn; // Close a document. +- (void) saveIn:(NSURL *)in_; // Save 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 object(s) and put the copies at a new location. +- (BOOL) exists; // Verify if an object exists. +- (void) moveTo:(SBObject *)to; // Move object(s) to a new location. + +@end + + + +/* + * Standard Suite + */ + +// The application‘s top-level scripting object. +@interface TerminalApplication : SBApplication + +- (SBElementArray *) windows; + +@property (copy, readonly) NSString *name; // The name of the application. +@property (readonly) BOOL frontmost; // Is this the frontmost (active) application? +@property (copy, readonly) NSString *version; // The version of the application. + +- (void) open:(NSArray *)x; // Open a document. +- (void) print:(id)x withProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog; // Print a document. +- (void) quitSaving:(TerminalSaveOptions)saving; // Quit the application. +- (TerminalTab *) doScript:(NSString *)x in:(id)in_; // Runs a UNIX shell script or command. + +@end + +// A window. +@interface TerminalWindow : SBObject + +- (SBElementArray *) tabs; + +@property (copy, readonly) NSString *name; // The full title of the window. +- (NSInteger) id; // The unique identifier of the window. +@property NSInteger index; // The index of the window, ordered front to back. +@property NSRect bounds; // The bounding rectangle of the window. +@property (readonly) BOOL closeable; // Whether the window has a close box. +@property (readonly) BOOL miniaturizable; // Whether the window can be minimized. +@property BOOL miniaturized; // Whether the window is currently minimized. +@property (readonly) BOOL resizable; // Whether the window can be resized. +@property BOOL visible; // Whether the window is currently visible. +@property (readonly) BOOL zoomable; // Whether the window can be zoomed. +@property BOOL zoomed; // Whether the window is currently zoomed. +@property BOOL frontmost; // Whether the window is currently the frontmost Terminal window. +@property (copy) TerminalTab *selectedTab; +@property NSPoint position; // The position of the window, relative to the upper left corner of the screen. +@property NSPoint origin; // The position of the window, relative to the lower left corner of the screen. +@property NSPoint size; // The width and height of the window +@property NSRect frame; // The bounding rectangle, relative to the lower left corner of the screen. + + +@end + + + +/* + * Terminal Suite + */ + +@interface TerminalApplication (TerminalSuite) + +- (SBElementArray *) settingsSets; + +@property (copy) TerminalSettingsSet *defaultSettings; // The settings set used for new windows. +@property (copy) TerminalSettingsSet *startupSettings; // The settings set used for the window created on application startup. + +@end + +// A set of settings. +@interface TerminalSettingsSet : SBObject + +- (NSInteger) id; // The unique identifier of the settings set. +@property (copy) NSString *name; // The name of the settings set. +@property NSInteger numberOfRows; // The number of rows displayed in the tab. +@property NSInteger numberOfColumns; // The number of columns displayed in the tab. +@property (copy) NSColor *cursorColor; // The cursor color for the tab. +@property (copy) NSColor *backgroundColor; // The background color for the tab. +@property (copy) NSColor *normalTextColor; // The normal text color for the tab. +@property (copy) NSColor *boldTextColor; // The bold text color for the tab. +@property (copy) NSString *fontName; // The name of the font used to display the tab’s contents. +@property NSInteger fontSize; // The size of the font used to display the tab’s contents. +@property BOOL fontAntialiasing; // Whether the font used to display the tab’s contents is antialiased. +@property (copy) NSArray *cleanCommands; // The processes which will be ignored when checking whether a tab can be closed without showing a prompt. +@property BOOL titleDisplaysDeviceName; // Whether the title contains the device name. +@property BOOL titleDisplaysShellPath; // Whether the title contains the shell path. +@property BOOL titleDisplaysWindowSize; // Whether the title contains the tab’s size, in rows and columns. +@property BOOL titleDisplaysSettingsName; // Whether the title contains the settings name. +@property BOOL titleDisplaysCustomTitle; // Whether the title contains a custom title. +@property (copy) NSString *customTitle; // The tab’s custom title. + + +@end + +// A tab. +@interface TerminalTab : SBObject + +@property NSInteger numberOfRows; // The number of rows displayed in the tab. +@property NSInteger numberOfColumns; // The number of columns displayed in the tab. +@property (copy, readonly) NSString *contents; // The currently visible contents of the tab. +@property (copy, readonly) NSString *history; // The contents of the entire scrolling buffer of the tab. +@property (readonly) BOOL busy; // Whether the tab is busy running a process. +@property (copy, readonly) NSArray *processes; // The processes currently running in the tab. +@property BOOL selected; // Whether the tab is selected. +@property BOOL titleDisplaysCustomTitle; // Whether the title contains a custom title. +@property (copy) NSString *customTitle; // The tab’s custom title. +@property (copy, readonly) NSString *tty; // The tab’s TTY device. +@property (copy) TerminalSettingsSet *currentSettings; // The set of settings which control the tab’s behavior and appearance. +@property (copy) NSColor *cursorColor; // The cursor color for the tab. +@property (copy) NSColor *backgroundColor; // The background color for the tab. +@property (copy) NSColor *normalTextColor; // The normal text color for the tab. +@property (copy) NSColor *boldTextColor; // The bold text color for the tab. +@property (copy) NSArray *cleanCommands; // The processes which will be ignored when checking whether a tab can be closed without showing a prompt. +@property BOOL titleDisplaysDeviceName; // Whether the title contains the device name. +@property BOOL titleDisplaysShellPath; // Whether the title contains the shell path. +@property BOOL titleDisplaysWindowSize; // Whether the title contains the tab’s size, in rows and columns. +@property BOOL titleDisplaysFileName; // Whether the title contains the file name. +@property (copy) NSString *fontName; // The name of the font used to display the tab’s contents. +@property NSInteger fontSize; // The size of the font used to display the tab’s contents. +@property BOOL fontAntialiasing; // Whether the font used to display the tab’s contents is antialiased. + + +@end + diff --git a/Classes/Util/GitXCommitCopier.h b/Classes/Util/GitXCommitCopier.h new file mode 100644 index 000000000..7c2179f63 --- /dev/null +++ b/Classes/Util/GitXCommitCopier.h @@ -0,0 +1,23 @@ +// +// GitXCommitCopier.h +// GitX +// +// Created by Sven-S. Porst on 20.12.15. +// +// + +#import + +@class PBGitCommit; + + +@interface GitXCommitCopier : NSValueTransformer + ++ (NSString * _Nonnull) toFullSHA:(NSArray * _Nonnull)commits; ++ (NSString * _Nonnull) toShortName:(NSArray * _Nonnull)commits; ++ (NSString * _Nonnull) toSHAAndHeadingString:(NSArray * _Nonnull)commits; ++ (NSString * _Nonnull) toPatch:(NSArray * _Nonnull)commits; + ++ (void) putStringToPasteboard:(NSString * _Nullable)string; + +@end diff --git a/Classes/Util/GitXCommitCopier.m b/Classes/Util/GitXCommitCopier.m new file mode 100644 index 000000000..5a49c7f5f --- /dev/null +++ b/Classes/Util/GitXCommitCopier.m @@ -0,0 +1,74 @@ +// +// GitXCommitCopier.m +// GitX +// +// Created by Sven-S. Porst on 20.12.15. +// +// + +#import "GitXCommitCopier.h" + +#import "PBGitCommit.h" + +@implementation GitXCommitCopier + + + +#pragma mark Readymade conversions + ++ (NSString *) toFullSHA:(NSArray *)commits { + NSArray *commitStrings = [self transformCommits:commits with:^(PBGitCommit * commit) { + return commit.SHA; + }]; + + return [commitStrings componentsJoinedByString:@"\n"]; +} + ++ (NSString *) toShortName:(NSArray *)commits { + NSArray *commitStrings = [self transformCommits:commits with:^(PBGitCommit * commit) { + return commit.shortName; + }]; + + return [commitStrings componentsJoinedByString:@" "]; +} + ++ (NSString *) toSHAAndHeadingString:(NSArray *)commits { + NSArray *commitStrings = [self transformCommits:commits with:^(PBGitCommit * commit) { + return [NSString stringWithFormat:@"%@ (%@)", [commit.SHA substringToIndex:10], commit.subject]; + }]; + + return [commitStrings componentsJoinedByString:@"\n"]; +} + ++ (NSString *) toPatch:(NSArray *)commits { + NSArray *commitStrings = [self transformCommits:commits with:^(PBGitCommit * commit) { + return commit.patch; + }]; + + return [commitStrings componentsJoinedByString:@"\n\n\n"]; +} + + + +# pragma mark Helpers + ++ (NSArray *) transformCommits:(NSArray *)commits with:(NSString *(^)(PBGitCommit * commit))transformer { + + NSMutableArray *strings = [NSMutableArray arrayWithCapacity:commits.count]; + [commits enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(PBGitCommit * _Nonnull commit, NSUInteger idx, BOOL * _Nonnull stop) { + [strings addObject:transformer(commit)]; + }]; + + return strings; +} + + ++ (void) putStringToPasteboard:(NSString *)string { + if (string.length > 0) { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:@[NSStringPboardType] owner:self]; + [pasteboard setString:string forType:NSStringPboardType]; + } +} + +@end diff --git a/Classes/Util/NSApplication+GitXScripting.h b/Classes/Util/NSApplication+GitXScripting.h new file mode 100644 index 000000000..fafab7e68 --- /dev/null +++ b/Classes/Util/NSApplication+GitXScripting.h @@ -0,0 +1,18 @@ +// +// NSApplication+GitXScripting.h +// GitX +// +// Created by Nathan Kinsinger on 8/15/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@interface NSApplication (GitXScripting) + +- (void)showDiffScriptCommand:(NSScriptCommand *)command; +- (void)initRepositoryScriptCommand:(NSScriptCommand *)command; +- (void)cloneRepositoryScriptCommand:(NSScriptCommand *)command; + +@end diff --git a/Classes/Util/NSApplication+GitXScripting.m b/Classes/Util/NSApplication+GitXScripting.m new file mode 100644 index 000000000..f0b5214de --- /dev/null +++ b/Classes/Util/NSApplication+GitXScripting.m @@ -0,0 +1,84 @@ +// +// NSApplication+GitXScripting.m +// GitX +// +// Created by Nathan Kinsinger on 8/15/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "NSApplication+GitXScripting.h" +#import "GitXScriptingConstants.h" +#import "PBDiffWindowController.h" +#import "PBGitRepository.h" +#import "PBCloneRepositoryPanel.h" +#import "PBGitBinary.h" +#import "PBTask.h" + + +@implementation NSApplication (GitXScripting) + +- (void)showDiffScriptCommand:(NSScriptCommand *)command +{ + NSString *diffText = [command directParameter]; + if (diffText) { + PBDiffWindowController *diffController = [[PBDiffWindowController alloc] initWithDiff:diffText]; + [diffController showWindow:nil]; + } +} + +- (void)performDiffScriptCommand:(NSScriptCommand *)command +{ + NSURL *repositoryURL = command.directParameter; + NSArray *diffOptions = command.arguments[@"diffOptions"]; + + diffOptions = [@[@"diff", @"--no-ext-diff"] arrayByAddingObjectsFromArray:diffOptions]; + + NSError *error = nil; + NSString *diffOutput = [PBTask outputForCommand:[PBGitBinary path] arguments:diffOptions inDirectory:repositoryURL.path error:&error]; + if (!diffOutput) { + // if there is an error diffOutput should have the error output from git + NSLog(@"Invalid diff command: %@", error); + return; + } + + PBDiffWindowController *diffController = [[PBDiffWindowController alloc] initWithDiff:diffOutput]; + [diffController showWindow:self]; +} + +- (void)initRepositoryScriptCommand:(NSScriptCommand *)command +{ + NSError *error = nil; + NSURL *repositoryURL = [command directParameter]; + if (!repositoryURL) + return; + + GTRepository *repo = [GTRepository initializeEmptyRepositoryAtFileURL:repositoryURL options:nil error:&error]; + if (!repo) { + NSLog(@"Failed to create repository at %@: %@", repositoryURL, error); + return; + } + + [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:repositoryURL + display:YES + completionHandler:^(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error) { + if (error) { + NSLog(@"Failed to open repository at %@: %@", repositoryURL, error); + } + }]; +} + +- (void)cloneRepositoryScriptCommand:(NSScriptCommand *)command +{ + NSString *repository = [command directParameter]; + if (repository) { + NSDictionary *arguments = [command arguments]; + NSURL *destinationURL = [arguments objectForKey:kGitXCloneDestinationURLKey]; + if (destinationURL) { + BOOL isBare = [[arguments objectForKey:kGitXCloneIsBareKey] boolValue]; + + [PBCloneRepositoryPanel beginCloneRepository:repository toURL:destinationURL isBare:isBare]; + } + } +} + +@end diff --git a/NSFileHandleExt.h b/Classes/Util/NSFileHandleExt.h similarity index 100% rename from NSFileHandleExt.h rename to Classes/Util/NSFileHandleExt.h diff --git a/NSFileHandleExt.m b/Classes/Util/NSFileHandleExt.m similarity index 65% rename from NSFileHandleExt.m rename to Classes/Util/NSFileHandleExt.m index 277285599..cf8a6782d 100644 --- a/NSFileHandleExt.m +++ b/Classes/Util/NSFileHandleExt.m @@ -27,36 +27,47 @@ -(NSString*)readLine { char *buffer = (char*)malloc(bufferSize + 1); if (buffer == NULL) [[NSException exceptionWithName:@"No memory left" reason:@"No more memory for allocating buffer" userInfo:nil] raise]; + buffer[0] = '\0'; - int bytesReceived = 0, n = 1; + int bytesReceived = 0; + long n = 0; - while (n > 0) { - n = read(fd, buffer + bytesReceived++, 1); + while (1) { + n = read(fd, buffer + bytesReceived, 1); - if (n < 0) - [[NSException exceptionWithName:@"Socket error" reason:@"Remote host closed connection" userInfo:nil] raise]; + if (n == 0) + break; + + if (n < 0) { + if (errno == EINTR) + continue; + + free(buffer); + NSString *reason = [NSString stringWithFormat:@"%s:%d: read() error: %s", __PRETTY_FUNCTION__, __LINE__, strerror(errno)]; + [[NSException exceptionWithName:@"Socket error" reason:reason userInfo:nil] raise]; + } + + bytesReceived++; if (bytesReceived >= bufferSize) { // Make buffer bigger bufferSize += BUFFER_SIZE; - buffer = (char*)realloc(buffer, bufferSize + 1); + buffer = (char *)reallocf(buffer, bufferSize + 1); if (buffer == NULL) [[NSException exceptionWithName:@"No memory left" reason:@"No more memory for allocating buffer" userInfo:nil] raise]; } - switch (*(buffer + bytesReceived - 1)) { - case '\n': - buffer[bytesReceived-1] = '\0'; - NSString* s = [NSString stringWithCString: buffer encoding: NSUTF8StringEncoding]; - if ([s length] == 0) - s = [NSString stringWithCString: buffer encoding: NSISOLatin1StringEncoding]; - return s; - case '\r': - bytesReceived--; + char receivedByte = buffer[bytesReceived-1]; + if (receivedByte == '\n') { + bytesReceived--; + break; } + + if (receivedByte == '\r') + bytesReceived--; } - buffer[bytesReceived-1] = '\0'; + buffer[bytesReceived] = '\0'; NSString *retVal = [NSString stringWithCString: buffer encoding: NSUTF8StringEncoding]; if ([retVal length] == 0) retVal = [NSString stringWithCString: buffer encoding: NSISOLatin1StringEncoding]; diff --git a/Classes/Util/NSOutlineViewExt.h b/Classes/Util/NSOutlineViewExt.h new file mode 100644 index 000000000..64c0186f0 --- /dev/null +++ b/Classes/Util/NSOutlineViewExt.h @@ -0,0 +1,15 @@ +// +// NSOutlineViewExit.h +// GitX +// +// Created by Pieter de Bie on 9/9/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface NSOutlineView (PBExpandParents) + +- (void)PBExpandItem:(id)item expandParents:(BOOL)expand; +@end diff --git a/Classes/Util/NSOutlineViewExt.m b/Classes/Util/NSOutlineViewExt.m new file mode 100644 index 000000000..57531f809 --- /dev/null +++ b/Classes/Util/NSOutlineViewExt.m @@ -0,0 +1,25 @@ +// +// NSOutlineViewExit.m +// GitX +// +// Created by Pieter de Bie on 9/9/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "NSOutlineViewExt.h" + + +@implementation NSOutlineView (PBExpandParents) + +- (void)PBExpandItem:(id)item expandParents:(BOOL)expand +{ + NSMutableArray *parents = [NSMutableArray array]; + while (item) { + [parents insertObject:item atIndex:0]; + item = [item parent]; + } + + for (id p in parents) + [self expandItem:p]; +} +@end diff --git a/Classes/Util/NSString_Truncate.h b/Classes/Util/NSString_Truncate.h new file mode 100644 index 000000000..45c156df9 --- /dev/null +++ b/Classes/Util/NSString_Truncate.h @@ -0,0 +1,32 @@ +// +// NSString_Truncate.h +// GitX +// +// Created by Andre Berg on 24.03.10. +// Copyright 2010 Berg Media. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +typedef enum { + PBNSStringTruncateModeCenter = 0, + PBNSStringTruncateModeStart = 1, + PBNSStringTruncateModeEnd = 2 +} PBNSStringTruncateMode; + +@interface NSString (PBGitXTruncateExtensions) + +- (NSString *) truncateToLength:(NSUInteger)length mode:(PBNSStringTruncateMode)mode indicator:(NSString *)indicatorString; + +@end diff --git a/Classes/Util/NSString_Truncate.m b/Classes/Util/NSString_Truncate.m new file mode 100644 index 000000000..f81520f85 --- /dev/null +++ b/Classes/Util/NSString_Truncate.m @@ -0,0 +1,69 @@ +// +// NSString_Truncate.m +// GitX +// +// Created by Andre Berg on 24.03.10. +// Copyright 2010 Berg Media. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "NSString_Truncate.h" + +@implementation NSString (PBGitXTruncateExtensions) + +- (NSString *) truncateToLength:(NSUInteger)targetLength mode:(PBNSStringTruncateMode)mode indicator:(NSString *)indicatorString { + + NSString * res = nil; + NSString * firstPart; + NSString * lastPart; + + if (!indicatorString) { + indicatorString = @"..."; + } + + NSUInteger stringLength = [self length]; + NSUInteger ilength = [indicatorString length]; + + if (stringLength <= targetLength) { + return self; + } else if (stringLength <= 0 || (!self)) { + return nil; + } else { + switch (mode) { + case PBNSStringTruncateModeCenter: + firstPart = [self substringToIndex:(targetLength/2)]; + lastPart = [self substringFromIndex:(stringLength-((targetLength/2))+ilength)]; + res = [NSString stringWithFormat:@"%@%@%@", firstPart, indicatorString, lastPart]; + break; + case PBNSStringTruncateModeStart: + res = [NSString stringWithFormat:@"%@%@", indicatorString, [self substringFromIndex:((stringLength-targetLength)+ilength)]]; + break; + case PBNSStringTruncateModeEnd: + res = [NSString stringWithFormat:@"%@%@", [self substringToIndex:(targetLength-ilength)], indicatorString]; + break; + default: + ; + NSException * myException = [NSException exceptionWithName:NSInvalidArgumentException + reason:[NSString stringWithFormat: + @"[%@ %@] called with nonsensical value for 'mode' (mode = %d) ***", + [self class], NSStringFromSelector(_cmd), mode] + userInfo:nil]; + @throw myException; + return res; + break; + }; + } + return res; +} + +@end diff --git a/Classes/Util/ObjectiveGit+PBCategories.h b/Classes/Util/ObjectiveGit+PBCategories.h new file mode 100644 index 000000000..390b2e248 --- /dev/null +++ b/Classes/Util/ObjectiveGit+PBCategories.h @@ -0,0 +1,17 @@ +// +// ObjectiveGit+PBCategories.h +// GitX +// +// Created by Etienne on 28/02/2017. +// +// + +#import + +@interface GTCommit (PBCategories) +- (NSArray *)parentOIDs; +@end + +@interface GTEnumerator (PBCategories) +- (BOOL)pushReferenceName:(NSString *)refName error:(NSError **)error; +@end diff --git a/Classes/Util/ObjectiveGit+PBCategories.m b/Classes/Util/ObjectiveGit+PBCategories.m new file mode 100644 index 000000000..b9264b521 --- /dev/null +++ b/Classes/Util/ObjectiveGit+PBCategories.m @@ -0,0 +1,34 @@ +// +// ObjectiveGit+PBCategories.h +// GitX +// +// Created by Etienne on 28/02/2017. +// +// + +#import "ObjectiveGit+PBCategories.h" + + +@implementation GTCommit (PBCategories) + +// This is an optimisation for the grapher. +// We're only interested in OIDs, and we don't need objects +- (NSArray *)parentOIDs { + unsigned numberOfParents = git_commit_parentcount(self.git_commit); + NSMutableArray *parents = [NSMutableArray arrayWithCapacity:numberOfParents]; + + for (unsigned i = 0; i < numberOfParents; i++) { + const git_oid *parent = git_commit_parent_id(self.git_commit, i); + + [parents addObject:[GTOID oidWithGitOid:parent]]; + } + + return parents; + +} + +@end + +@interface GTEnumerator (Private) +@property (nonatomic, assign, readonly) git_revwalk *walk; +@end diff --git a/PBEasyFS.h b/Classes/Util/PBEasyFS.h similarity index 83% rename from PBEasyFS.h rename to Classes/Util/PBEasyFS.h index 0007fa440..aec8609a6 100644 --- a/PBEasyFS.h +++ b/Classes/Util/PBEasyFS.h @@ -12,7 +12,7 @@ @interface PBEasyFS : NSObject { } -+ (NSString*) tmpNameWithSuffix: (NSString*) path; + + (NSString*) tmpDirWithPrefix: (NSString*) path; @end diff --git a/PBEasyFS.m b/Classes/Util/PBEasyFS.m similarity index 60% rename from PBEasyFS.m rename to Classes/Util/PBEasyFS.m index ed9c30b6d..8d339de25 100644 --- a/PBEasyFS.m +++ b/Classes/Util/PBEasyFS.m @@ -11,15 +11,6 @@ @implementation PBEasyFS -+ (NSString*) tmpNameWithSuffix: (NSString*) path -{ - NSString* newName = [NSString stringWithFormat: @"%@/XXXXXX%@", NSTemporaryDirectory(), path]; - char *template = (char*) [newName fileSystemRepresentation]; - int fd = mkstemps(template, [path length]); - close(fd); - return [NSString stringWithUTF8String:template]; -} - + (NSString*) tmpDirWithPrefix: (NSString*) path { NSString* newName = [NSString stringWithFormat: @"%@%@.XXXXXX", NSTemporaryDirectory(), path]; diff --git a/Classes/Util/PBEasyPipe.h b/Classes/Util/PBEasyPipe.h new file mode 100644 index 000000000..8685c2a38 --- /dev/null +++ b/Classes/Util/PBEasyPipe.h @@ -0,0 +1,35 @@ +// +// PBEasyPipe.h +// GitX +// +// Created by Pieter de Bie on 16-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PBEasyPipe: NSObject + +/* The following methods are kept for backward-compatibility. + * Newly-written code should use the block-based methods above. + */ ++ (NSTask *)taskForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args inDir:(nullable NSString *)dir GITX_DEPRECATED; ++ (NSString *)outputForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args GITX_DEPRECATED; ++ (NSString *)outputForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args inDir:(nullable NSString *)dir GITX_DEPRECATED; ++ (NSString *)outputForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args inDir:(nullable NSString *)dir retValue:(nullable int *)ret GITX_DEPRECATED; ++ (NSString *)outputForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args inDir:(nullable NSString *)dir inputString:(nullable NSString *)input retValue:(nullable int *)ret GITX_DEPRECATED; ++ (NSString *)outputForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args inDir:(nullable NSString *)dir byExtendingEnvironment:(nullable NSDictionary *)dict inputString:(nullable NSString *)input retValue:(nullable int *)ret GITX_DEPRECATED; + +/* + * The following methods are deprecated because they're inherently racy: + * They are launched at the end of the method, but you might not be able to + * register for the NSFileHandle notification before they are done running. + */ ++ (NSFileHandle *)handleForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args GITX_DEPRECATED; ++ (NSFileHandle *)handleForCommand:(NSString *)cmd withArgs:(nullable NSArray *)args inDir:(nullable NSString *)dir GITX_DEPRECATED; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/PBEasyPipe.m b/Classes/Util/PBEasyPipe.m similarity index 52% rename from PBEasyPipe.m rename to Classes/Util/PBEasyPipe.m index 3b80184d6..1ba80c33f 100644 --- a/PBEasyPipe.m +++ b/Classes/Util/PBEasyPipe.m @@ -8,44 +8,39 @@ #import "PBEasyPipe.h" +#pragma mark - PBEasyPipe implementation @implementation PBEasyPipe -+ (NSFileHandle*) handleForCommand: (NSString*) cmd withArgs: (NSArray*) args ++ (NSTask *)taskForCommand:(NSString *)cmd arguments:(NSArray *)args inDirectory:(NSString *)directory { - return [self handleForCommand:cmd withArgs:args inDir:nil]; -} + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath:cmd]; + [task setArguments:args]; -+ (NSTask *) taskForCommand:(NSString *)cmd withArgs:(NSArray *)args inDir:(NSString *)dir -{ - NSTask* task = [[NSTask alloc] init]; - task.launchPath = cmd; - task.arguments = args; - if (dir) - task.currentDirectoryPath = dir; + // Prepare ourselves a nicer environment + NSMutableDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy]; + [env removeObjectsForKeys:@[@"MallocStackLogging", + @"MallocStackLoggingNoCompact", + @"DYLD_INSERT_LIBRARIES", // to avoid GuardMalloc logging + @"NSZombieEnabled"]]; + [task setEnvironment:env]; + + if (directory) + [task setCurrentDirectoryPath:directory]; if ([[NSUserDefaults standardUserDefaults] boolForKey:@"Show Debug Messages"]) - NSLog(@"Starting command `%@ %@` in dir %@", cmd, [args componentsJoinedByString:@" "], dir); + NSLog(@"Starting command `%@ %@` in dir %@", cmd, [args componentsJoinedByString:@" "], directory); #ifdef CLI - NSLog(@"Starting command `%@ %@` in dir %@", cmd, [args componentsJoinedByString:@" "], dir); + NSLog(@"Starting command `%@ %@` in dir %@", cmd, [args componentsJoinedByString:@" "], directory); #endif - NSPipe* pipe = [NSPipe pipe]; - task.standardOutput = pipe; + NSPipe *pipe = [NSPipe pipe]; + [task setStandardOutput:pipe]; + [task setStandardError:pipe]; return task; } -+ (NSFileHandle*) handleForCommand: (NSString*) cmd withArgs: (NSArray*) args inDir: (NSString*) dir -{ - NSTask *task = [self taskForCommand:cmd withArgs:args inDir:dir]; - NSFileHandle* handle = [task.standardOutput fileHandleForReading]; - - [task launch]; - return handle; -} - - - + (NSString*) outputForCommand:(NSString *) cmd withArgs:(NSArray *) args inDir:(NSString *) dir @@ -70,24 +65,39 @@ + (NSString*) outputForCommand:(NSString *) cmd inputString:(NSString *) input retValue:(int *) ret { - NSTask *task = [self taskForCommand:cmd withArgs:args inDir:dir]; + NSTask *task = [self taskForCommand:cmd arguments:args inDirectory:dir]; if (dict) { NSMutableDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy]; [env addEntriesFromDictionary:dict]; - task.environment = env; + [task setEnvironment:env]; } - NSFileHandle* handle = [task.standardOutput fileHandleForReading]; + NSFileHandle* handle = [[task standardOutput] fileHandleForReading]; + NSFileHandle *inHandle = nil; if (input) { - task.standardInput = [NSPipe pipe]; - NSFileHandle *inHandle = [task.standardInput fileHandleForWriting]; - [inHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]]; - [inHandle closeFile]; + [task setStandardInput:[NSPipe pipe]]; + inHandle = [[task standardInput] fileHandleForWriting]; } - [task launch]; + @try { + [task launch]; + } + @catch (NSException *exception) { + if (ret) *ret = -1; + return nil; + } + + if (input && inHandle) { + // A large write could wait for stdout buffer to be flushed by the task, + // which may not happen until the task is run. The task may similarly wait + // for its stdout to be read before reading its stdin, causing a deadlock. + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + [inHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]]; + [inHandle closeFile]; + }); + } NSData* data = [handle readDataToEndOfFile]; NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; @@ -109,17 +119,17 @@ + (NSString*) outputForCommand:(NSString *) cmd + (NSString*) outputForCommand: (NSString*) cmd withArgs: (NSArray*) args inDir: (NSString*) dir { - NSTask *task = [self taskForCommand:cmd withArgs:args inDir:dir]; - NSFileHandle* handle = [task.standardOutput fileHandleForReading]; + NSTask *task = [self taskForCommand:cmd arguments:args inDirectory:dir]; + NSFileHandle* handle = [[task standardOutput] fileHandleForReading]; [task launch]; -#warning This can cause a "Bad file descriptor"... when? - NSData *data; + // This can cause a "Bad file descriptor"... when? + NSData *data = nil; @try { data = [handle readDataToEndOfFile]; } @catch (NSException * e) { - NSLog(@"Got a bad file descriptor in %s!", _cmd); + NSLog(@"Got a bad file descriptor in %@!", NSStringFromSelector(_cmd)); if ([NSThread currentThread] != [NSThread mainThread]) [task waitUntilExit]; @@ -139,10 +149,29 @@ + (NSString*) outputForCommand: (NSString*) cmd withArgs: (NSArray*) args inDir return string; } - -+ (NSString*) outputForCommand: (NSString*) cmd withArgs: (NSArray*) args ++ (NSString *)outputForCommand:(NSString *)cmd withArgs:(NSArray *)args { return [self outputForCommand:cmd withArgs:args inDir:nil]; } +/* Deprecated */ + ++ (NSTask *)taskForCommand:(NSString *)cmd withArgs:(NSArray *)args inDir:(NSString *)dir { + return [self taskForCommand:cmd arguments:args inDirectory:dir]; +} + ++ (NSFileHandle *)handleForCommand:(NSString *)cmd withArgs:(NSArray *)arguments +{ + return [self handleForCommand:cmd withArgs:arguments inDir:nil]; +} + ++ (NSFileHandle *)handleForCommand:(NSString *)cmd withArgs:(NSArray *)args inDir:(NSString *)dir +{ + NSTask *task = [self taskForCommand:cmd arguments:args inDirectory:dir]; + NSFileHandle* handle = [[task standardOutput] fileHandleForReading]; + + [task launch]; + return handle; +} + @end diff --git a/Classes/Util/PBError.h b/Classes/Util/PBError.h new file mode 100644 index 000000000..2f84a58b7 --- /dev/null +++ b/Classes/Util/PBError.h @@ -0,0 +1,101 @@ +// +// PBError.h +// GitX +// +// Created by Andre Berg on 31.10.09. +// Copyright 2009 Berg Media. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * const PBGitXErrorDomain; + +@interface NSError (PBError) + +/** + * GitX helper for error creation. + * + * @see pb_errorWithDescription:failureReason:underlyingError:userInfo: + */ ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason; + +/** + * GitX helper for error creation, with user info. + * + * @see pb_errorWithDescription:failureReason:underlyingError:userInfo: + */ ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason userInfo:(nullable NSDictionary *)userInfo; + +/** + * GitX helper for error creation, with underlying error. + * + * @see pb_errorWithDescription:failureReason:underlyingError:userInfo: + */ ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason underlyingError:(nullable NSError *)underError; + +/** GitX helper for error creation, with underlying error and user info. + * + * This uses a @p 0 error code and the @p PBGitXErrorDomain domain as defaults. + * + * @notes + * The values set as @p description, @p failureReason and @p underlyingError are + * used in priority over those in the @p userInfo dictionary (if any). + * + * @param description A quick description of the error. + * @param failureReason A more verbose explanation. + * @param underlyingError An error to set as the underlying error. + * @param userInfo A dictionary to use as the error userInfo. + * + * @return A newly initialized NSError instance. + */ ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason underlyingError:(nullable NSError *)underError userInfo:(nullable NSDictionary *)userInfo; +@end + +/** + * Easily handle NSError double-pointers. + * + * @param error The error parameter to fill. + * @param description A quick description of the error. + * @param failureReason A more verbose explanation. + * @param underlyingError An error to set as the underlying error. + * + * @return NO. + */ +BOOL PBReturnError(NSError **error, NSString *description, NSString *failureReason, NSError * __nullable underlyingError); + +/** + * Helper function for easily handling NSError double-pointers. + * + * @param error The error parameter to fill. + * @param description A quick description of the error. + * @param failureReason A more verbose explanation. + * @param userInfo A dictionary to use as the error userInfo. + * + * @return NO. + */ +BOOL PBReturnErrorWithUserInfo(NSError **error, NSString *description, NSString *failureReason, NSDictionary * _Nullable userInfo); + +/** + * Helper function for easily handling NSError double-pointers. + * + * @param error The error parameter to fill. + * @param builder A block responsible to create a valid NSError object. + * + * @return NO. + */ +BOOL PBReturnErrorWithBuilder(NSError **error, NSError * (^errorBuilder)(void)); + +NS_ASSUME_NONNULL_END diff --git a/Classes/Util/PBError.m b/Classes/Util/PBError.m new file mode 100644 index 000000000..a55ab2dc4 --- /dev/null +++ b/Classes/Util/PBError.m @@ -0,0 +1,81 @@ +// +// PBError.m +// GitX +// +// Created by Andre Berg on 31.10.09. +// Copyright 2009 Berg Media. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "PBError.h" + +NSString * const PBGitXErrorDomain = @"PBGitXErrorDomain"; + +@implementation NSError (PBError) + ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason +{ + return [self pb_errorWithDescription:description failureReason:failureReason underlyingError:nil userInfo:nil]; +} + ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason userInfo:(nullable NSDictionary *)userInfo +{ + return [self pb_errorWithDescription:description failureReason:failureReason underlyingError:nil userInfo:userInfo]; +} + ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason underlyingError:(NSError *)underError +{ + return [self pb_errorWithDescription:description failureReason:failureReason underlyingError:underError userInfo:nil]; +} + ++ (NSError *)pb_errorWithDescription:(NSString *)description failureReason:(NSString *)failureReason underlyingError:(NSError *)underError userInfo:(NSDictionary *)userInfo { + NSParameterAssert(description != nil); + NSParameterAssert(failureReason != nil); + + NSMutableDictionary *errorInfo = userInfo ? [userInfo mutableCopy] : [NSMutableDictionary dictionary]; + + [errorInfo addEntriesFromDictionary:@{ + NSLocalizedDescriptionKey: description, + NSLocalizedFailureReasonErrorKey: failureReason, + }]; + + if (underError) { + [errorInfo addEntriesFromDictionary:@{ NSUnderlyingErrorKey: underError }]; + } + + return [NSError errorWithDomain:PBGitXErrorDomain code:0 userInfo:errorInfo]; +} + +@end + + +BOOL PBReturnError(NSError **error, NSString *description, NSString *failureReason, NSError *underlyingError) { + if (error) { + *error = [NSError pb_errorWithDescription:description failureReason:failureReason underlyingError:underlyingError]; + } + return NO; +} + +BOOL PBReturnErrorWithUserInfo(NSError **error, NSString *description, NSString *failureReason, NSDictionary *userInfo) { + if (error) { + *error = [NSError pb_errorWithDescription:description failureReason:failureReason userInfo:userInfo]; + } + return NO; +} + +BOOL PBReturnErrorWithBuilder(NSError **error, NSError * (^errorBuilder)(void)) { + if (error) { + *error = errorBuilder(); + } + return NO; +} diff --git a/Classes/Util/PBTask.h b/Classes/Util/PBTask.h new file mode 100644 index 000000000..88a5a3077 --- /dev/null +++ b/Classes/Util/PBTask.h @@ -0,0 +1,130 @@ +// +// PBTask.h +// GitX +// +// Created by Etienne on 22/02/2017. +// +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const PBTaskErrorDomain; +extern NSString *const PBTaskUnderlyingExceptionKey; +extern NSString *const PBTaskTerminationStatusKey; +extern NSString *const PBTaskTerminationOutputKey; + +typedef NS_ENUM(NSUInteger, PBTaskErrorCode) { + PBTaskLaunchError = 1, + PBTaskTimeoutError = 2, + PBTaskCaughtSignalError = 3, + PBTaskNonZeroExitCodeError = 4, +}; + +/// PBTask is a wrapper around NSTask that uses blocks to report when the +/// executable exits, and should be used whenever we need to shell out to git. +@interface PBTask : NSObject + +/// +/// Setup a task for subsequent execution. +/// +/// @param launchPath The absolute path to the executable that should be run. +/// @param arguments The arguments to pass to the executable. Can be nil. +/// @param directory The directory to use as the executable working directory. Can be nil. +/// ++ (instancetype)taskWithLaunchPath:(NSString *)launchPath arguments:(nullable NSArray *)arguments inDirectory:(nullable NSString *)directory; + +/// +/// Execute a task. +/// +/// @param queue The queue on which the handler will be called. +/// @param terminationHandler A block that will be called when the executable exits. +/// +- (void)performTaskOnQueue:(dispatch_queue_t)queue terminationHandler:(void (^)(NSError * __nullable error))terminationHandler; + +/// +/// Execute a task, and process its output +/// +/// @param queue The queue on which the handler will be called. +/// @param completionHandler A block that will be called when the executable exits. +/// If readData is nil, it means an error occurred. +/// +- (void)performTaskOnQueue:(dispatch_queue_t)queue completionHandler:(void (^)(NSData * __nullable readData, NSError * __nullable error))completionHandler; + +/// Execute a task synchronously +/// +/// This method will block until the task exits, or a timeout occurs (30s by default). +/// @param error If the command failed to complete, the pointer will be set to +/// an error object describing the reason +/// +/// @return YES if the command execution was successful, no otherwise +/// +- (BOOL)launchTask:(NSError **)error; + +/// The standard output of the command +@property (readonly, retain) NSData *standardOutputData; +/// Set this if you want to pass data to the command on its standard input +@property (retain) NSData *standardInputData; +@property (retain) NSDictionary *additionalEnvironment; + +- (void)terminate; + +@end + +@interface PBTask (PBMainQueuePerform) + +/// Execute a task +/// +/// This uses the main queue +/// +/// @see performTaskOnQueue:terminationHandler: +- (void)performTaskWithTerminationHandler:(void (^)(NSError * __nullable error))terminationHandler; + +/// Execute a task +/// +/// This uses the main queue +/// +/// @see performTaskOnQueue:completionHandler: +- (void)performTaskWithCompletionHandler:(void (^)(NSData * __nullable readData, NSError * __nullable error))completionHandler; + +@end + +@interface PBTask (PBBellsAndWhistles) + +/// Execute a command, only caring for its output +/// +/// @see -[PBTask outputForCommand:arguments:inDirectory:error] +/// ++ (nullable NSString *)outputForCommand:(NSString *)launchPath arguments:(nullable NSArray *)arguments error:(NSError **)error; + + +/// Execute a command, only caring for its output +/// +/// @param launchPath The absolute path to the executable that should be run. +/// @param arguments The arguments to pass to the executable. Can be nil. +/// @param directory The directory to use as the executable working directory. Can be nil. +/// +/// @return The data outputted by the command as a string, nil in case of failure. +/// Hint: We only try to convert from UTF-8 data, so that might fail. +/// ++ (nullable NSString *)outputForCommand:(NSString *)launchPath arguments:(nullable NSArray *)arguments inDirectory:(nullable NSString *)directory error:(NSError **)error; + +/// Directly launch a task with a completion handler +/// +/// @param launchPath The absolute path to the executable that should be run. +/// @param arguments The arguments to pass to the executable. Can be nil. +/// @param directory The directory to use as the executable working directory. Can be nil. +/// @param completionHandler A block that will be called when the executable exits. +/// If readData is nil, it means an error occurred. +/// ++ (void)launchTask:(NSString *)launchPath arguments:(nullable NSArray *)arguments inDirectory:(nullable NSString *)directory completionHandler:(void (^)(NSData * __nullable readData, NSError * __nullable error))completionHandler; + +/// Return the standard output data as a string +/// +/// If the data is not valid UTF-8, nil will be returned. +/// +- (nullable NSString *)standardOutputString; +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/Util/PBTask.m b/Classes/Util/PBTask.m new file mode 100644 index 000000000..e8d6e8dc5 --- /dev/null +++ b/Classes/Util/PBTask.m @@ -0,0 +1,283 @@ +// +// PBTask.m +// GitX +// +// Created by Etienne on 22/02/2017. +// +// + +#import "PBTask.h" + +NSString *const PBTaskErrorDomain = @"PBTaskErrorDomain"; +NSString *const PBTaskUnderlyingExceptionKey = @"PBTaskUnderlyingExceptionKey"; +NSString *const PBTaskTerminationStatusKey = @"PBTaskTerminationStatusKey"; +NSString *const PBTaskTerminationOutputKey = @"PBTaskTerminationOutputKey"; + +const BOOL PBTaskDebugEnable = NO; + +#define PBTaskLog(...) \ +do { \ + if (PBTaskDebugEnable) NSLog(__VA_ARGS__); \ +} while (0) + +@interface PBTask () + +@property (retain) NSTask *task; +@property (retain) NSMutableData *standardOutputData; + +@end + +@implementation PBTask + ++ (instancetype)taskWithLaunchPath:(NSString *)launchPath arguments:(NSArray *)arguments inDirectory:(NSString *)directory { + return [[self alloc] initWithLaunchPath:launchPath arguments:arguments inDirectory:directory]; +} + +- (instancetype)initWithLaunchPath:(NSString *)launchPath arguments:(NSArray *)args inDirectory:(NSString *)directory +{ + self = [super init]; + if (!self) return nil; + + _task = [[NSTask alloc] init]; + [_task setLaunchPath:launchPath]; + [_task setArguments:args]; + + // Prepare ourselves a nicer environment + NSMutableDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy]; + [env removeObjectsForKeys:@[ + @"DYLD_INSERT_LIBRARIES", @"DYLD_LIBRARY_PATH", + @"MallocGuardEdges", @"MallocNanoZone", @"MallocScribble", @"MallocStackLogging", @"MallocStackLoggingNoCompact", + @"NSZombieEnabled"] + ]; + if (self.additionalEnvironment) { + [env addEntriesFromDictionary:self.additionalEnvironment]; + } + [_task setEnvironment:env]; + + if (directory) + [_task setCurrentDirectoryPath:directory]; + + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"Show Debug Messages"]) + NSLog(@"Starting command `%@ %@` in dir %@", launchPath, [args componentsJoinedByString:@" "], directory); +#ifdef CLI + NSLog(@"Starting command `%@ %@` in dir %@", launchPath, [args componentsJoinedByString:@" "], directory); +#endif + + NSPipe *pipe = [NSPipe pipe]; + [_task setStandardOutput:pipe]; + [_task setStandardError:pipe]; + + _standardOutputData = [NSMutableData data]; + __weak PBTask *weakSelf = self; + pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *handle) { + PBTaskLog(@"task %p: can read %d", weakSelf, handle.fileDescriptor); + + NSData *data = handle.availableData; + if (data.length) { + @synchronized (weakSelf) { + [(NSMutableData *)weakSelf.standardOutputData appendData:data]; + } + } else { + PBTaskLog(@"task %p: EOF, closing %d", weakSelf, handle.fileDescriptor); + [handle closeFile]; + } + }; + + PBTaskLog(@"task %p: init", self); + + return self; +} + +- (void)dealloc { + PBTaskLog(@"task %p: dealloc", self); +} + + +- (void)performTaskOnQueue:(dispatch_queue_t)queue terminationHandler:(void (^)(NSError * _Nullable))terminationHandler { + NSParameterAssert(terminationHandler != nil); + + __weak PBTask *weakSelf = self; + self.task.terminationHandler = ^(NSTask *task) { + NSError *error = nil; + if (task.terminationReason == NSTaskTerminationReasonUncaughtSignal) { + PBTaskLog(@"task %p: caught signal", weakSelf); + + NSString *desc = @"Task killed"; + NSArray *taskArguments = [@[task.launchPath] arrayByAddingObjectsFromArray:task.arguments]; + NSString *failureReason = [NSString stringWithFormat:@"The task \"%@\" caught a termination signal", [taskArguments componentsJoinedByString:@" "]]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: failureReason, + }; + error = [NSError errorWithDomain:PBTaskErrorDomain code:PBTaskCaughtSignalError userInfo:userInfo]; + + } else if (task.terminationReason == NSTaskTerminationReasonExit && task.terminationStatus != 0) { + // Since we're on an error path, grab the output now and stash it in the returned error + + PBTaskLog(@"task %p: exit != 0", weakSelf); + + NSString *outputString = [[NSString alloc] initWithData:weakSelf.standardOutputData encoding:NSUTF8StringEncoding]; + weakSelf.standardOutputData = nil; + + NSString *desc = @"Task exited unsuccessfully"; + NSArray *taskArguments = [@[task.launchPath] arrayByAddingObjectsFromArray:task.arguments]; + NSString *failureReason = [NSString stringWithFormat:@"The task \"%@\" returned a non-zero return code", [taskArguments componentsJoinedByString:@" "]]; + int status = task.terminationStatus; + NSNumber *terminationStatus = (status < 255 ? [NSNumber numberWithShort:(short)status] : @(status)); + + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: failureReason, + PBTaskTerminationStatusKey: terminationStatus, + PBTaskTerminationOutputKey: outputString, + }; + error = [NSError errorWithDomain:PBTaskErrorDomain code:PBTaskNonZeroExitCodeError userInfo:userInfo]; + } else { + PBTaskLog(@"task %p: exit success", weakSelf); + } + + dispatch_async(queue, ^{ + terminationHandler(error); + }); + }; + + if (self.standardInputData) { + NSPipe *inputPipe = [NSPipe pipe]; + + self.task.standardInput = inputPipe; + + inputPipe.fileHandleForWriting.writeabilityHandler = ^(NSFileHandle *handle) { + PBTaskLog(@"task %p: can write %d", weakSelf, handle.fileDescriptor); + + [handle writeData:weakSelf.standardInputData]; + [handle closeFile]; + }; + } + + @try { + PBTaskLog(@"task %p: launching", self); + [self.task launch]; + } + @catch (NSException *exception) { + NSString *desc = @"Exception raised while launching task"; + NSString *failureReason = [NSString stringWithFormat:@"The task \"%@\" failed to launch", self.task.launchPath]; + NSDictionary *info = @{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: failureReason, + PBTaskUnderlyingExceptionKey: exception, + }; + NSError *error = [NSError errorWithDomain:PBTaskErrorDomain + code:PBTaskLaunchError + userInfo:info]; + + dispatch_async(queue, ^{ + terminationHandler(error); + }); + } +} + +- (void)performTaskOnQueue:(dispatch_queue_t)queue completionHandler:(void (^)(NSData *readData, NSError *error))completionHandler { + [self performTaskOnQueue:queue terminationHandler:^(NSError *error) { + if (error) { + completionHandler(nil, error); + return; + } + + @synchronized (self) { + PBTaskLog(@"task %p: completed, removing read handler", self); + [self.task.standardOutput fileHandleForReading].readabilityHandler = nil; + } + + completionHandler(self.standardOutputData, nil); + }]; +} + +- (BOOL)launchTask:(NSError **)error { + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + + __block NSError *taskError = nil; + + [self performTaskOnQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) + completionHandler:^(NSData *readData, NSError *error) { + + taskError = error; + + dispatch_semaphore_signal(sem); + }]; + + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC); + PBTaskLog(@"task %p: waiting for completion", self); + if (dispatch_semaphore_wait(sem, timeout) != 0) { + // Timeout ! + // Unset the termination handler before calling, so we don't trigger it + self.task.terminationHandler = nil; + [self terminate]; + + if (error) { + NSString *desc = @"Timeout while running task"; + NSArray *taskArguments = [@[self.task.launchPath] arrayByAddingObjectsFromArray:self.task.arguments]; + NSString *failureReason = [NSString stringWithFormat:@"The task \"%@\" failed to complete before its timeout", taskArguments]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: failureReason, + }; + *error = [NSError errorWithDomain:PBTaskErrorDomain code:PBTaskTimeoutError userInfo:userInfo]; + } + + return NO; + } + + if (error) *error = taskError; + return (taskError == nil); +} + +- (void)terminate { + [self.task terminate]; +} + +- (NSString *)description { + NSArray *taskArguments = [@[self.task.launchPath] arrayByAddingObjectsFromArray:self.task.arguments]; + return [NSString stringWithFormat:@"<%@ %p command: %@ stdin: %@>", NSStringFromClass([self class]), self, + [taskArguments componentsJoinedByString:@" "], + (self.standardInputData ? @"YES" : @"NO") + ]; +} + +@end + +@implementation PBTask (PBBellsAndWhistles) + ++ (NSString *)outputForCommand:(NSString *)launchPath arguments:(NSArray *)arguments error:(NSError **)error { + return [self outputForCommand:launchPath arguments:arguments inDirectory:nil error:error]; +} + ++ (NSString *)outputForCommand:(NSString *)launchPath arguments:(NSArray *)arguments inDirectory:(NSString *)directory error:(NSError **)error { + PBTask *task = [self taskWithLaunchPath:launchPath arguments:arguments inDirectory:directory]; + BOOL success = [task launchTask:error]; + if (!success) return nil; + + return task.standardOutputString; +} + ++ (void)launchTask:(NSString *)launchPath arguments:(NSArray *)arguments inDirectory:(NSString *)directory completionHandler:(void (^)(NSData *readData, NSError *error))completionHandler { + PBTask *task = [self taskWithLaunchPath:launchPath arguments:arguments inDirectory:directory]; + [task performTaskWithCompletionHandler:completionHandler]; +} + +- (NSString *)standardOutputString { + return [[NSString alloc] initWithData:self.standardOutputData encoding:NSUTF8StringEncoding]; +} + +@end + +@implementation PBTask (PBMainQueuePerform) + +- (void)performTaskWithTerminationHandler:(void (^)(NSError *error))terminationHandler { + [self performTaskOnQueue:dispatch_get_main_queue() terminationHandler:terminationHandler]; +} + +- (void)performTaskWithCompletionHandler:(void (^)(NSData * __nullable readData, NSError * __nullable error))completionHandler { + [self performTaskOnQueue:dispatch_get_main_queue() completionHandler:completionHandler]; +} + +@end diff --git a/Classes/Util/PBTerminalUtil.h b/Classes/Util/PBTerminalUtil.h new file mode 100644 index 000000000..3f965f70a --- /dev/null +++ b/Classes/Util/PBTerminalUtil.h @@ -0,0 +1,19 @@ +// +// PBTerminalUtil.h +// GitX +// +// Created by Sven on 07.08.16. +// +// + +#import + +@interface PBTerminalUtil : NSObject + +/* + * Runs the given command in OS X’s Terminal.app + * at the given directory. + */ ++ (void) runCommand:(NSString *)command inDirectory:(NSURL *)directory; + +@end diff --git a/Classes/Util/PBTerminalUtil.m b/Classes/Util/PBTerminalUtil.m new file mode 100644 index 000000000..f25692b36 --- /dev/null +++ b/Classes/Util/PBTerminalUtil.m @@ -0,0 +1,29 @@ +// +// PBTerminalUtil.m +// GitX +// +// Created by Sven on 07.08.16. +// + +#import "PBTerminalUtil.h" +#import "Terminal.h" + +@implementation PBTerminalUtil + ++ (NSString *) initialCommand:(NSURL *)workingDirectory { + return [NSString stringWithFormat:@"cd \"%@\"; clear; echo '# Opened by GitX'; ", + workingDirectory.path]; +} + ++ (void) runCommand:(NSString *)command inDirectory:(NSURL *)directory { + NSString * initialCommand = [self initialCommand:directory]; + NSString * fullCommand = [initialCommand stringByAppendingString:command]; + + TerminalApplication *term = [SBApplication applicationWithBundleIdentifier: @"com.apple.Terminal"]; + [term doScript:fullCommand in: nil]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [term activate]; + }); +} + +@end \ No newline at end of file diff --git a/Classes/Util/RJModalRepoSheet.h b/Classes/Util/RJModalRepoSheet.h new file mode 100644 index 000000000..25b3b69f5 --- /dev/null +++ b/Classes/Util/RJModalRepoSheet.h @@ -0,0 +1,56 @@ +// +// RJModalRepoSheet.h +// GitX +// +// Created by Rowan James on 1/7/12. +// Copyright (c) 2012 Phere Development Pty. Ltd. All rights reserved. +// + +#import + +@class PBGitRepository; +@class PBGitWindowController; +@class PBGitRepositoryDocument; + +NS_ASSUME_NONNULL_BEGIN + +@interface RJModalRepoSheet : NSWindowController + +@property (nonnull, strong) PBGitWindowController *windowController; +@property (nonnull, assign) PBGitRepositoryDocument *document; +@property (nonnull, readonly) PBGitRepository *repository; + +- (instancetype)initWithWindowNibName:(NSString *)windowNibName windowController:(PBGitWindowController *)windowController; +- (instancetype)initWithWindowNibName:(NSString *)windowNibName; + +- (instancetype)init NS_UNAVAILABLE; + +typedef void(^RJSheetCompletionHandler)(id sheet, NSModalResponse returnCode); + +- (void)beginSheetWithCompletionHandler:(nullable RJSheetCompletionHandler)handler; + +/** + * Temporarily hide the sheet. + * + * You must call -show afterward, or the initial completion handler will not be called. + */ +- (void)hide; + +/** + * Dismiss the sheet. + * + * This will cause the handler to be called with an NSModalResponseAbort code. + */ +- (void)dismiss; + +/** + * Redisplay a hidden sheet. + */ +- (void)show; + +- (IBAction)acceptSheet:(nullable id)sender; +- (IBAction)cancelSheet:(nullable id)sender; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/Util/RJModalRepoSheet.m b/Classes/Util/RJModalRepoSheet.m new file mode 100644 index 000000000..16cce191d --- /dev/null +++ b/Classes/Util/RJModalRepoSheet.m @@ -0,0 +1,128 @@ +// +// RJModalRepoSheet.m +// GitX +// +// Created by Rowan James on 1/7/12. +// Copyright (c) 2012 Phere Development Pty. Ltd. All rights reserved. +// + +#import "RJModalRepoSheet.h" + +#import "PBGitRepository.h" +#import "PBGitWindowController.h" +#import "PBGitRepositoryDocument.h" + +@interface RJModalRepoSheet () { + BOOL _hasWindowController; +} + +@property (copy) RJSheetCompletionHandler completionHandler; + +@end + +@implementation RJModalRepoSheet + +@dynamic document; + +- (instancetype)initWithWindowNibName:(NSString *)windowNibName +{ + return [super initWithWindowNibName:windowNibName]; +} + +- (instancetype)initWithWindowNibName:(NSString *)windowNibName windowController:(nonnull PBGitWindowController *)windowController +{ + NSParameterAssert(windowController != nil); + + self = [super initWithWindowNibName:windowNibName owner:self]; + if (!self) return nil; + + _windowController = windowController; + _hasWindowController = YES; + + return self; +} + +- (PBGitRepositoryDocument *)document { + return self.windowController.document; +} + +- (PBGitRepository *)repository +{ + return self.document.repository; +} + +- (void)beginSheetWithCompletionHandler:(RJSheetCompletionHandler)handler +{ + // Stash the completion handler so we can setup this sheet again in -show + self.completionHandler = handler; + + [self presentSheet]; +} + +- (void)presentSheet +{ + NSAssert(!_hasWindowController || self.windowController != nil, @"-beginSheetWithCompletionHandler: called with nil windowController"); + + void (^modalHandler)(NSModalResponse returnCode) = ^(NSModalResponse returnCode) { + if (returnCode == NSModalResponseStop) { + // Something called -hide on us, because it needed to display another sheet. + // Don't call our handler, because we're not actually done yet. + return; + } + + if (self.completionHandler) { + self.completionHandler(self, returnCode); + } + }; + + if (_hasWindowController) { + [self.windowController.window beginSheet:self.window completionHandler:modalHandler]; + } else { + NSInteger modalResponseMaybe = [NSApp runModalForWindow:self.window]; + modalHandler(modalResponseMaybe); + } +} + +- (void)endSheetWithReturnCode:(NSModalResponse)returnCode +{ + NSAssert(!_hasWindowController || self.windowController != nil, @"-endSheetWithReturnCode: called with nil windowController"); + + if (_hasWindowController) { + [self.windowController.window endSheet:self.window returnCode:returnCode]; + } else { + [NSApp endSheet:self.window returnCode:returnCode]; + } +} + +- (IBAction)acceptSheet:(id)sender +{ + [self endSheetWithReturnCode:NSModalResponseOK]; +} + +- (IBAction)cancelSheet:(id)sender +{ + [self endSheetWithReturnCode:NSModalResponseCancel]; +} + +- (void)show +{ + [self presentSheet]; +} + +- (void)hide +{ + [self endSheetWithReturnCode:NSModalResponseStop]; +} + +- (void)dismiss +{ + [self endSheetWithReturnCode:NSModalResponseAbort]; +} + +// For Cmd-. support +- (IBAction)cancelOperation:(id)sender +{ + [self cancelSheet:self]; +} + +@end diff --git a/Classes/Views/GLFileView.h b/Classes/Views/GLFileView.h new file mode 100644 index 000000000..f0b879d49 --- /dev/null +++ b/Classes/Views/GLFileView.h @@ -0,0 +1,27 @@ +// +// GLFileView.h +// GitX +// +// Created by German Laullon on 14/09/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBWebController.h" + +@class PBGitHistoryController; + +@interface GLFileView : PBWebController { + __weak IBOutlet PBGitHistoryController* historyController; + __weak IBOutlet NSView *accessoryView; + __weak IBOutlet NSSplitView *fileListSplitView; +} + +- (void)showFile; +- (void)didLoad; +- (NSString *)parseBlame:(NSString *)txt; +- (NSString *)escapeHTML:(NSString *)txt; + +@property NSMutableArray *groups; + +@end diff --git a/Classes/Views/GLFileView.m b/Classes/Views/GLFileView.m new file mode 100644 index 000000000..0e88bb535 --- /dev/null +++ b/Classes/Views/GLFileView.m @@ -0,0 +1,283 @@ +// +// GLFileView.m +// GitX +// +// Created by German Laullon on 14/09/10. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#import "GLFileView.h" +#import "PBGitTree.h" +#import "PBGitCommit.h" +#import "PBGitHistoryController.h" + + +#define GROUP_LABEL @"Label" // string +#define GROUP_SEPARATOR @"HasSeparator" // BOOL as NSNumber +#define GROUP_SELECTION_MODE @"SelectionMode" // MGScopeBarGroupSelectionMode (int) as NSNumber +#define GROUP_ITEMS @"Items" // array of dictionaries, each containing the following keys: +#define ITEM_IDENTIFIER @"Identifier" // string +#define ITEM_NAME @"Name" // string + +#define GROUP_ID_FILEVIEW @"fileview" +#define GROUP_ID_BLAME @"blame" +#define GROUP_ID_LOG @"log" + +@interface GLFileView () + +- (void)saveSplitViewPosition; + +@end + + +@implementation GLFileView + +- (void) awakeFromNib +{ + startFile = GROUP_ID_FILEVIEW; + //repository = historyController.repository; + [super awakeFromNib]; + [historyController.treeController addObserver:self forKeyPath:@"selection" options:0 context:@"treeController"]; + + [fileListSplitView setHidden:YES]; + [self performSelector:@selector(restoreSplitViewPositiion) withObject:nil afterDelay:0]; +} + +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + //NSLog(@"keyPath=%@ change=%@ context=%@ object=%@ \n %@",keyPath,change,context,object,[historyController.treeController selectedObjects]); + [self showFile]; +} + +- (void) showFile +{ + NSArray *files=[historyController.treeController selectedObjects]; + if ([files count] > 0) { + PBGitTree *file = [files objectAtIndex:0]; + + NSString *fileTxt = @""; + if([startFile isEqualToString:GROUP_ID_FILEVIEW]) + fileTxt = [self escapeHTML:[file textContents]]; + else if([startFile isEqualToString:GROUP_ID_BLAME]) + fileTxt = [self parseBlame:[file blame]]; + else if([startFile isEqualToString:GROUP_ID_LOG]) + fileTxt = [self htmlHistory:file]; + + id script = self.view.windowScriptObject; + NSString *filePath = [file fullPath]; + [script callWebScriptMethod:@"showFile" withArguments:[NSArray arrayWithObjects:fileTxt, filePath, nil]]; + } + +#if 0 + NSString *dom=[[[[view mainFrame] DOMDocument] documentElement] outerHTML]; + NSString *tmpFile=@"~/tmp/test.html"; + [dom writeToFile:[tmpFile stringByExpandingTildeInPath] atomically:true encoding:NSUTF8StringEncoding error:nil]; +#endif +} + +#pragma mark JavaScript log.js methods + +- (void) selectCommit:(NSString*)c +{ + [historyController selectCommit: [GTOID oidWithSHA: c]]; +} + +- (void) didLoad +{ + [self showFile]; +} + +- (void)closeView +{ + [historyController.treeController removeObserver:self forKeyPath:@"selection"]; + [self saveSplitViewPosition]; + + [super closeView]; +} + +- (NSString *) escapeHTML:(NSString *)txt +{ + CFStringRef escaped = CFXMLCreateStringByEscapingEntities(NULL, (__bridge CFStringRef)txt, NULL); + return (__bridge_transfer NSString *)escaped; +} + +- (NSString *) parseBlame:(NSString *)txt +{ + txt=[self escapeHTML:txt]; + + NSArray *lines = [txt componentsSeparatedByString:@"\n"]; + NSString *line; + NSMutableDictionary *headers=[NSMutableDictionary dictionary]; + NSMutableString *res=[NSMutableString string]; + + [res appendString:@"\n"]; + int i=0; + while(i<[lines count]){ + line=[lines objectAtIndex:i]; + NSArray *header=[line componentsSeparatedByString:@" "]; + if([header count]==4){ + NSString *commitID = (NSString *)[header objectAtIndex:0]; + int nLines=[(NSString *)[header objectAtIndex:3] intValue]; + [res appendFormat:@"\n",nLines]; + line=[lines objectAtIndex:++i]; + if([[[line componentsSeparatedByString:@" "] objectAtIndex:0] isEqual:@"author"]){ + NSString *author=[line stringByReplacingOccurrencesOfString:@"author" withString:@""]; + NSString *summary=nil; + while(summary==nil){ + line=[lines objectAtIndex:i++]; + if([[[line componentsSeparatedByString:@" "] objectAtIndex:0] isEqual:@"summary"]){ + summary=[line stringByReplacingOccurrencesOfString:@"summary" withString:@""]; + } + } + NSRange trunc_c={0,7}; + NSString *truncate_c=commitID; + if([commitID length]>8){ + truncate_c=[commitID substringWithRange:trunc_c]; + } + NSRange trunc={0,22}; + NSString *truncate_a=author; + if([author length]>22){ + truncate_a=[author substringWithRange:trunc]; + } + NSString *truncate_s=summary; + if([summary length]>30){ + truncate_s=[summary substringWithRange:trunc]; + } + NSString *block=[NSString stringWithFormat:@"\n\n"]; + }else{ + break; + } + [res appendString:@"\n"]; + } + [res appendString:@"

%@ %@

%@

\n",commitID,truncate_c,truncate_a,truncate_s]; + [headers setObject:block forKey:[header objectAtIndex:0]]; + } + [res appendString:[headers objectForKey:[header objectAtIndex:0]]]; + + NSMutableString *code=[NSMutableString string]; + do{ + line=[lines objectAtIndex:i++]; + }while([line characterAtIndex:0]!='\t'); + line=[line substringFromIndex:1]; + line=[line stringByReplacingOccurrencesOfString:@"\t" withString:@"    "]; + [code appendString:line]; + [code appendString:@"\n"]; + + int n; + for(n=1;n%@",[header objectAtIndex:2],code]; + [res appendString:@"
\n"]; + //NSLog(@"%@",res); + + return (NSString *)res; +} + +- (NSString *) htmlHistory:(PBGitTree *)file +{ + // \0 can't be passed as a shell argument, so use a sufficiently long random seperator instead + NSString *seperator = [[NSUUID UUID] UUIDString]; + NSString *commitTerminator = [[NSUUID UUID] UUIDString]; + NSString *logFormat = [[@"%h,%s,%aN,%ar,%H" stringByReplacingOccurrencesOfString:@"," withString:seperator] stringByAppendingString:commitTerminator]; + NSString *output = [file log:logFormat]; + NSArray *rawCommits = [output componentsSeparatedByString:commitTerminator]; + rawCommits = [rawCommits subarrayWithRange:(NSRange){0, rawCommits.count - 1}]; + + NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet]; + + NSMutableString *html = [NSMutableString string]; + for (NSString *rawCommit in rawCommits) { + NSArray *parts = [rawCommit componentsSeparatedByString:seperator]; + [html appendFormat: + @"
" + "

%@

" + "" + "" + "" + "" + "
Author:%@
Date:%@
Commit:%@
" + "
", + [self escapeHTML:[parts[0] stringByTrimmingCharactersInSet:whitespaceSet]], // trim leading newline from split + [self escapeHTML:parts[1]], + [self escapeHTML:parts[2]], + [self escapeHTML:parts[3]], + [self escapeHTML:parts[4]]]; + } + return html; +} + + +#pragma mark NSSplitView delegate methods + +#define kFileListSplitViewLeftMin 120 +#define kFileListSplitViewRightMin 180 +#define kHFileListSplitViewPositionDefault @"File List SplitView Position" + +- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex +{ + return kFileListSplitViewLeftMin; +} + +- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex +{ + return [splitView frame].size.width - [splitView dividerThickness] - kFileListSplitViewRightMin; +} + +// while the user resizes the window keep the left (file list) view constant and just resize the right view +// unless the right view gets too small +- (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize +{ + NSRect newFrame = [splitView frame]; + + CGFloat dividerThickness = [splitView dividerThickness]; + + NSView *leftView = [[splitView subviews] objectAtIndex:0]; + NSRect leftFrame = [leftView frame]; + leftFrame.size.height = newFrame.size.height; + + if ((newFrame.size.width - leftFrame.size.width - dividerThickness) < kFileListSplitViewRightMin) { + leftFrame.size.width = newFrame.size.width - kFileListSplitViewRightMin - dividerThickness; + } + + NSView *rightView = [[splitView subviews] objectAtIndex:1]; + NSRect rightFrame = [rightView frame]; + rightFrame.origin.x = leftFrame.size.width + dividerThickness; + rightFrame.size.width = newFrame.size.width - rightFrame.origin.x; + rightFrame.size.height = newFrame.size.height; + + [leftView setFrame:leftFrame]; + [rightView setFrame:rightFrame]; +} + +// NSSplitView does not save and restore the position of the SplitView correctly so do it manually +- (void)saveSplitViewPosition +{ + CGFloat position = [[[fileListSplitView subviews] objectAtIndex:0] frame].size.width; + [[NSUserDefaults standardUserDefaults] setDouble:position forKey:kHFileListSplitViewPositionDefault]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +// make sure this happens after awakeFromNib +- (void)restoreSplitViewPositiion +{ + CGFloat position = [[NSUserDefaults standardUserDefaults] doubleForKey:kHFileListSplitViewPositionDefault]; + if (position < 1.0) + position = 200; + + [fileListSplitView setPosition:position ofDividerAtIndex:0]; + [fileListSplitView setHidden:NO]; +} + + + +@synthesize groups; + +@end diff --git a/Classes/Views/GitXTextFieldCell.h b/Classes/Views/GitXTextFieldCell.h new file mode 100644 index 000000000..c2e192551 --- /dev/null +++ b/Classes/Views/GitXTextFieldCell.h @@ -0,0 +1,17 @@ +// +// GitXTextFieldCell.h +// GitX +// +// Created by Nathan Kinsinger on 8/27/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + +@protocol PBRefContextDelegate; + +@interface GitXTextFieldCell : NSTextFieldCell { + IBOutlet id contextMenuDelegate; +} + +@end diff --git a/Classes/Views/GitXTextFieldCell.m b/Classes/Views/GitXTextFieldCell.m new file mode 100644 index 000000000..35b340e55 --- /dev/null +++ b/Classes/Views/GitXTextFieldCell.m @@ -0,0 +1,38 @@ +// +// GitXTextFieldCell.m +// GitX +// +// Created by Nathan Kinsinger on 8/27/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "GitXTextFieldCell.h" +#import "PBGitCommit.h" +#import "PBRefController.h" +#import "PBRefContextDelegate.h" + + +@implementation GitXTextFieldCell + +- (NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + // disables the cell's selection highlight + return nil; +} + +- (NSMenu *)menuForEvent:(NSEvent *)anEvent inRect:(NSRect)cellFrame ofView:(NSTableView *)commitList +{ + NSInteger rowIndex = [commitList rowAtPoint:(cellFrame.origin)]; + NSArray *items = [contextMenuDelegate menuItemsForRow:rowIndex]; + if (!items) + return nil; + + NSMenu *menu = [[NSMenu alloc] init]; + [menu setAutoenablesItems:NO]; + for (NSMenuItem *item in items) + [menu addItem:item]; + + return menu; +} + +@end diff --git a/Classes/Views/GitXTextView.h b/Classes/Views/GitXTextView.h new file mode 100644 index 000000000..7b2bd50cd --- /dev/null +++ b/Classes/Views/GitXTextView.h @@ -0,0 +1,12 @@ +// +// GitXTextView.h +// GitX +// +// Created by NanoTech on 2016-12-14. +// + +#import + +/** A text view that remembers text substitution preferences. */ +@interface GitXTextView : NSTextView +@end diff --git a/Classes/Views/GitXTextView.m b/Classes/Views/GitXTextView.m new file mode 100644 index 000000000..c7fbc793b --- /dev/null +++ b/Classes/Views/GitXTextView.m @@ -0,0 +1,92 @@ +// +// GitXTextView.m +// GitX +// +// Created by NanoTech on 2016-12-14. +// + +#import "GitXTextView.h" + +static NSString *AutomaticDashSubstitutionEnabledKey = @"GitXTextViewAutomaticDashSubstitutionEnabled"; +static NSString *AutomaticDataDetectionEnabledKey = @"GitXTextViewAutomaticDataDetectionEnabled"; +static NSString *AutomaticLinkDetectionEnabledKey = @"GitXTextViewAutomaticLinkDetectionEnabled"; +static NSString *AutomaticQuoteSubstitutionEnabled = @"GitXTextViewAutomaticQuoteSubstitutionEnabled"; +static NSString *AutomaticSpellingCorrectionEnabledKey = @"GitXTextViewAutomaticSpellingCorrectionEnabled"; +static NSString *AutomaticTextReplacementEnabledKey = @"GitXTextViewAutomaticTextReplacementEnabled"; +static NSString *SmartInsertDeleteEnabledKey = @"GitXTextViewSmartInsertDeleteEnabled"; // "Smart Copy/Paste" + +@implementation GitXTextView + ++ (void)initialize +{ + if (self != [GitXTextView class]) return; + + // Matches the commit message text view properties in PBGitCommitView.xib + [[NSUserDefaults standardUserDefaults] registerDefaults:@{ + AutomaticDashSubstitutionEnabledKey : @(NO), + AutomaticDataDetectionEnabledKey : @(NO), + AutomaticLinkDetectionEnabledKey : @(YES), + AutomaticQuoteSubstitutionEnabled : @(NO), + AutomaticSpellingCorrectionEnabledKey : @(NO), + AutomaticTextReplacementEnabledKey : @(NO), + SmartInsertDeleteEnabledKey : @(YES), + }]; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + super.automaticDashSubstitutionEnabled = [defaults boolForKey:AutomaticDashSubstitutionEnabledKey]; + super.automaticDataDetectionEnabled = [defaults boolForKey:AutomaticDataDetectionEnabledKey]; + super.automaticLinkDetectionEnabled = [defaults boolForKey:AutomaticLinkDetectionEnabledKey]; + super.automaticQuoteSubstitutionEnabled = [defaults boolForKey:AutomaticQuoteSubstitutionEnabled]; + super.automaticSpellingCorrectionEnabled = [defaults boolForKey:AutomaticSpellingCorrectionEnabledKey]; + super.automaticTextReplacementEnabled = [defaults boolForKey:AutomaticTextReplacementEnabledKey]; + super.smartInsertDeleteEnabled = [defaults boolForKey:SmartInsertDeleteEnabledKey]; +} + +- (void)setAutomaticDashSubstitutionEnabled:(BOOL)enabled +{ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:AutomaticDashSubstitutionEnabledKey]; + [super setAutomaticDashSubstitutionEnabled:enabled]; +} + +- (void)setAutomaticDataDetectionEnabled:(BOOL)enabled +{ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:AutomaticDataDetectionEnabledKey]; + [super setAutomaticDataDetectionEnabled:enabled]; +} + +- (void)setAutomaticLinkDetectionEnabled:(BOOL)enabled +{ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:AutomaticLinkDetectionEnabledKey]; + [super setAutomaticLinkDetectionEnabled:enabled]; +} + +- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)enabled +{ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:AutomaticQuoteSubstitutionEnabled]; + [super setAutomaticQuoteSubstitutionEnabled:enabled]; +} + +- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)enabled +{ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:AutomaticSpellingCorrectionEnabledKey]; + [super setAutomaticSpellingCorrectionEnabled:enabled]; +} + +- (void)setAutomaticTextReplacementEnabled:(BOOL)enabled +{ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:AutomaticTextReplacementEnabledKey]; + [super setAutomaticTextReplacementEnabled:enabled]; +} + +- (void)setSmartInsertDeleteEnabled:(BOOL)enabled +{ + [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:SmartInsertDeleteEnabledKey]; + [super setSmartInsertDeleteEnabled:enabled]; +} + +@end diff --git a/Classes/Views/PBAddRemoteSheet.h b/Classes/Views/PBAddRemoteSheet.h new file mode 100644 index 000000000..2a33e4065 --- /dev/null +++ b/Classes/Views/PBAddRemoteSheet.h @@ -0,0 +1,31 @@ +// +// PBAddRemoteSheet.h +// GitX +// +// Created by Nathan Kinsinger on 12/8/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import + +#import "RJModalRepoSheet.h" + +@class PBGitWindowController; + +@interface PBAddRemoteSheet : RJModalRepoSheet + ++ (void)beginSheetWithWindowController:(PBGitWindowController *)windowController completionHandler:(RJSheetCompletionHandler)handler; + +- (IBAction) browseFolders:(id)sender; +- (IBAction) addRemote:(id)sender; +- (IBAction) showHideHiddenFiles:(id)sender; +- (IBAction) cancelOperation:(id)sender; + +@property (readwrite, weak) IBOutlet NSTextField *remoteName; +@property (readwrite, weak) IBOutlet NSTextField *remoteURL; +@property (readwrite, weak) IBOutlet NSTextField *errorMessage; + +@property (readwrite, strong) NSOpenPanel *browseSheet; +@property (readwrite, strong) IBOutlet NSView *browseAccessoryView; + +@end diff --git a/Classes/Views/PBAddRemoteSheet.m b/Classes/Views/PBAddRemoteSheet.m new file mode 100644 index 000000000..51e2cc038 --- /dev/null +++ b/Classes/Views/PBAddRemoteSheet.m @@ -0,0 +1,95 @@ +// +// PBAddRemoteSheet.m +// GitX +// +// Created by Nathan Kinsinger on 12/8/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import "PBAddRemoteSheet.h" +#import "PBGitWindowController.h" +#import "PBGitRepository.h" +#import "PBGitRepositoryDocument.h" + + +@implementation PBAddRemoteSheet + +#pragma mark - +#pragma mark PBAddRemoteSheet + ++ (void)beginSheetWithWindowController:(PBGitWindowController *)windowController completionHandler:(RJSheetCompletionHandler)handler +{ + PBAddRemoteSheet *sheet = [[super alloc] initWithWindowNibName:@"PBAddRemoteSheet" windowController:windowController]; + [sheet beginSheetWithCompletionHandler:handler]; +} + +- (void)beginSheetWithCompletionHandler:(RJSheetCompletionHandler)handler +{ + [self.errorMessage setStringValue:@""]; + [super beginSheetWithCompletionHandler:handler]; +} + +#pragma mark IBActions + +- (IBAction) browseFolders:(id)sender +{ + PBAddRemoteSheet *me = self; + NSOpenPanel *browseSheet = [NSOpenPanel openPanel]; + + [browseSheet setTitle:NSLocalizedString(@"Add remote", @"Title of sheet to enter data for a new remote")]; + [browseSheet setMessage:NSLocalizedString(@"Select a folder with a git repository", @"Title of sheet to enter data for a new remote")]; + [browseSheet setCanChooseFiles:NO]; + [browseSheet setCanChooseDirectories:YES]; + [browseSheet setAllowsMultipleSelection:NO]; + [browseSheet setCanCreateDirectories:NO]; + [browseSheet setAccessoryView:me.browseAccessoryView]; + + self.browseSheet = browseSheet; + [me hide]; + [browseSheet beginSheetModalForWindow:self.windowController.window + completionHandler:^(NSInteger result) { + if (result == NSModalResponseOK) { + NSString* directory = browseSheet.directoryURL.path; + [me.remoteURL setStringValue:directory]; + } + [me show]; + }]; +} + + +- (IBAction) addRemote:(id)sender +{ + [self.errorMessage setStringValue:@""]; + + NSString *name = [[self.remoteName stringValue] copy]; + + if ([name isEqualToString:@""]) { + [self.errorMessage setStringValue:NSLocalizedString(@"Remote name is required", @"Add Remote error message: missing name")]; + return; + } + + if (![self.repository checkRefFormat:[@"refs/remotes/" stringByAppendingString:name]]) { + [self.errorMessage setStringValue:NSLocalizedString(@"Invalid remote name", @"Add Remote error message: invalid name")]; + return; + } + + NSString *url = [[self.remoteURL stringValue] copy]; + if ([url isEqualToString:@""]) { + [self.errorMessage setStringValue:NSLocalizedString(@"Remote URL is required", @"Add Remote error message: missing URL")]; + return; + } + + [self acceptSheet:sender]; +} + +- (IBAction) showHideHiddenFiles:(id)sender +{ + [self.browseSheet setShowsHiddenFiles:[sender state] == NSOnState]; +} + +- (IBAction) cancelOperation:(id)sender +{ + [self cancelSheet:sender]; +} + +@end diff --git a/Classes/Views/PBCloneRepositoryPanel.h b/Classes/Views/PBCloneRepositoryPanel.h new file mode 100644 index 000000000..d8b3490d9 --- /dev/null +++ b/Classes/Views/PBCloneRepositoryPanel.h @@ -0,0 +1,38 @@ +// +// PBCloneRepositoryPanel.h +// GitX +// +// Created by Nathan Kinsinger on 2/7/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@interface PBCloneRepositoryPanel : NSWindowController { + NSOpenPanel *browseRepositoryPanel; + NSOpenPanel *browseDestinationPanel; + + NSString *path; + BOOL isBare; +} + ++ (id) panel; ++ (void)beginCloneRepository:(NSString *)repository toURL:(NSURL *)targetURL isBare:(BOOL)bare; + +- (void)showErrorSheet:(NSError *)error; + +- (IBAction) closeCloneRepositoryPanel:(id)sender; +- (IBAction) clone:(id)sender; +- (IBAction) browseRepository:(id)sender; +- (IBAction) showHideHiddenFiles:(id)sender; +- (IBAction) browseDestination:(id)sender; + +@property (nonatomic, weak) IBOutlet NSTextField *repositoryURL; +@property (nonatomic, weak) IBOutlet NSTextField *destinationPath; +@property (nonatomic, weak) IBOutlet NSTextField *errorMessage; +@property (nonatomic, weak) IBOutlet NSView *repositoryAccessoryView; + +@property (assign) BOOL isBare; + +@end diff --git a/Classes/Views/PBCloneRepositoryPanel.m b/Classes/Views/PBCloneRepositoryPanel.m new file mode 100644 index 000000000..55d555965 --- /dev/null +++ b/Classes/Views/PBCloneRepositoryPanel.m @@ -0,0 +1,201 @@ +// +// PBCloneRepositoryPanel.m +// GitX +// +// Created by Nathan Kinsinger on 2/7/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBCloneRepositoryPanel.h" +#import "PBRemoteProgressSheet.h" +#import "PBGitDefaults.h" + + + +@implementation PBCloneRepositoryPanel + + +@synthesize repositoryURL; +@synthesize destinationPath; +@synthesize errorMessage; +@synthesize repositoryAccessoryView; + +@synthesize isBare; + + + +#pragma mark - +#pragma mark PBCloneRepositoryPanel + ++ (id) panel +{ + return [[self alloc] initWithWindowNibName:@"PBCloneRepositoryPanel"]; +} + ++ (void)beginCloneRepository:(NSString *)repository toURL:(NSURL *)targetURL isBare:(BOOL)bare +{ + if (!repository || [repository isEqualToString:@""] || !targetURL || [[targetURL path] isEqualToString:@""]) + return; + + PBCloneRepositoryPanel *clonePanel = [PBCloneRepositoryPanel panel]; + [clonePanel showWindow:self]; + + [clonePanel.repositoryURL setStringValue:repository]; + [clonePanel.destinationPath setStringValue:[targetURL path]]; + clonePanel.isBare = bare; + + [clonePanel clone:self]; +} + + +- (void) awakeFromNib +{ + [self window]; + [self.errorMessage setStringValue:@""]; + path = [PBGitDefaults recentCloneDestination]; + if (path) + [self.destinationPath setStringValue:path]; + + browseRepositoryPanel = [NSOpenPanel openPanel]; + [browseRepositoryPanel setTitle:NSLocalizedString(@"Browse for git repository", @"Title for the file selector sheet for the source on the local file system to clone _from_")]; + [browseRepositoryPanel setMessage:NSLocalizedString(@"Select a folder with a git repository", @"Message on the file selector sheet to clone a repository from the local file system")]; + [browseRepositoryPanel setPrompt:NSLocalizedString(@"Select", @"Select (directory on local file system to clone a new repository from)")]; + [browseRepositoryPanel setCanChooseFiles:NO]; + [browseRepositoryPanel setCanChooseDirectories:YES]; + [browseRepositoryPanel setAllowsMultipleSelection:NO]; + [browseRepositoryPanel setCanCreateDirectories:NO]; + [browseRepositoryPanel setAccessoryView:repositoryAccessoryView]; + + browseDestinationPanel = [NSOpenPanel openPanel]; + [browseDestinationPanel setTitle:NSLocalizedString(@"Browse clone destination", @"Title for the file selector sheet for the destination of a clone operation")]; + [browseDestinationPanel setMessage:NSLocalizedString(@"Select a folder to clone the git repository into", @"Message on the file selector sheet for the destination of a clone operation")]; + [browseDestinationPanel setPrompt:NSLocalizedString(@"Select", @"Select (destination to clone a new repository to)")]; + [browseDestinationPanel setCanChooseFiles:NO]; + [browseDestinationPanel setCanChooseDirectories:YES]; + [browseDestinationPanel setAllowsMultipleSelection:NO]; + [browseDestinationPanel setCanCreateDirectories:YES]; +} + + +- (void)showErrorSheet:(NSError *)error +{ + [[NSAlert alertWithError:error] beginSheetModalForWindow:[self window] + modalDelegate:self + didEndSelector:@selector(errorSheetDidEnd:returnCode:contextInfo:) + contextInfo:NULL]; +} + + + +#pragma mark IBActions + +- (IBAction) closeCloneRepositoryPanel:(id)sender +{ + [self close]; +} + + +- (IBAction) clone:(id)sender +{ + [self.errorMessage setStringValue:@""]; + + NSString *url = [self.repositoryURL stringValue]; + if ([url isEqualToString:@""]) { + [self.errorMessage setStringValue:NSLocalizedString(@"Repository URL is required", @"Error message for missing source location when starting a clone operation")]; + return; + } + + path = [self.destinationPath stringValue]; + if ([path isEqualToString:@""]) { + [self.errorMessage setStringValue:NSLocalizedString(@"Destination path is required", @"Error message for missing target location when starting a clone operation")]; + return; + } + + NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"clone", @"--", url, path, nil]; + if (isBare) + [arguments insertObject:@"--bare" atIndex:1]; + + NSString *title = NSLocalizedString(@"Cloning Repository", @"Title of clone dialogue while clone is running"); + NSString *description = [NSString stringWithFormat:NSLocalizedString(@"Cloning repository at: %@", @"Message in clone dialogue while clone is running."), url]; + + + NSURL *documentURL = [NSURL fileURLWithPath:path]; + PBRemoteProgressSheet *sheet = [PBRemoteProgressSheet progressSheetWithTitle:title description:description]; + [sheet beginProgressSheetForBlock:^NSError *{ + NSURL *repoURL = [NSURL URLWithString:url]; + NSError *error = nil; + GTRepository *repo = [GTRepository cloneFromURL:repoURL + toWorkingDirectory:documentURL + options:@{GTRepositoryCloneOptionsBare: @(self.isBare)} + error:&error + transferProgressBlock:nil]; + if (!repo) + return error; + return nil; + } completionHandler:^(NSError *error) { + if (error) { + [self close]; + [self showErrorSheet:error]; + return; + } + + [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:documentURL display:YES completionHandler:^(NSDocument * _Nullable document, BOOL documentWasAlreadyOpen, NSError * _Nullable error) { + if (!document && error) { + [self showErrorSheet:error]; + return; + } + + [self close]; + + NSString *containingPath = [self->path stringByDeletingLastPathComponent]; + [PBGitDefaults setRecentCloneDestination:containingPath]; + [self.destinationPath setStringValue:containingPath]; + [self.repositoryURL setStringValue:@""]; + }]; + }]; +} + + +- (IBAction) browseRepository:(id)sender +{ + [browseRepositoryPanel beginSheetModalForWindow:[self window] + completionHandler:^(NSInteger result) { + if (result == NSModalResponseOK) { + NSURL *url = [[self->browseRepositoryPanel URLs] lastObject]; + [self.repositoryURL setStringValue:[url path]]; + } + }]; +} + + +- (IBAction) showHideHiddenFiles:(id)sender +{ + // This uses undocumented OpenPanel features to show hidden files (required for 10.5 support) + NSNumber *showHidden = [NSNumber numberWithBool:[sender state] == NSOnState]; + [[browseRepositoryPanel valueForKey:@"_navView"] setValue:showHidden forKey:@"showsHiddenFiles"]; +} + + +- (IBAction) browseDestination:(id)sender +{ + [browseDestinationPanel beginSheetModalForWindow:[self window] + completionHandler:^(NSInteger result) { + if (result == NSModalResponseOK) { + NSURL *url = [[self->browseDestinationPanel URLs] lastObject]; + [self.destinationPath setStringValue:[url path]]; + } + }]; +} + + + +#pragma mark Callbacks + + +- (void) errorSheetDidEnd:(NSOpenPanel *)sheet returnCode:(NSInteger)code contextInfo:(void *)info +{ + [self close]; +} + + +@end diff --git a/Classes/Views/PBCommitHookFailedSheet.h b/Classes/Views/PBCommitHookFailedSheet.h new file mode 100644 index 000000000..31fecff0e --- /dev/null +++ b/Classes/Views/PBCommitHookFailedSheet.h @@ -0,0 +1,26 @@ +// +// PBCommitHookFailedSheet.h +// GitX +// +// Created by Sebastian Staudt on 9/12/10. +// Copyright 2010 Sebastian Staudt. All rights reserved. +// + +#import + +#import "PBGitCommitController.h" +#import "PBGitXMessageSheet.h" + + +@interface PBCommitHookFailedSheet : PBGitXMessageSheet + ++ (void)beginWithMessageText:(NSString *)message + infoText:(NSString *)info + commitController:(PBGitCommitController *)controller + completionHandler:(RJSheetCompletionHandler)handler; + +- (IBAction)forceCommit:(id)sender; + +@property (nonatomic, strong) PBGitCommitController* commitController; + +@end \ No newline at end of file diff --git a/Classes/Views/PBCommitHookFailedSheet.m b/Classes/Views/PBCommitHookFailedSheet.m new file mode 100644 index 000000000..93ef177cc --- /dev/null +++ b/Classes/Views/PBCommitHookFailedSheet.m @@ -0,0 +1,55 @@ +// +// PBCommitHookFailedSheet.m +// GitX +// +// Created by Sebastian Staudt on 9/12/10. +// Copyright 2010 Sebastian Staudt. All rights reserved. +// + +#import "PBCommitHookFailedSheet.h" +#import "PBGitWindowController.h" + + +@implementation PBCommitHookFailedSheet + +@synthesize commitController; + +#pragma mark - +#pragma mark PBCommitHookFailedSheet + + ++ (void)beginWithMessageText:(NSString *)message + infoText:(NSString *)info + commitController:(PBGitCommitController *)controller + completionHandler:(RJSheetCompletionHandler)handler; +{ + PBCommitHookFailedSheet* sheet = [[self alloc] initWithWindowNibName:@"PBCommitHookFailedSheet" + andController:controller]; + [sheet beginMessageSheetWithMessageText:message + infoText:info + completionHandler:handler]; +} + +- (id)initWithWindowNibName:(NSString*)windowNibName + andController:(PBGitCommitController*)controller; +{ + self = [self initWithWindowNibName:windowNibName windowController:controller.windowController]; + if (!self) + return nil; + + self.commitController = controller; + + return self; +} + +- (IBAction)forceCommit:(id)sender +{ + [self acceptSheet:sender]; +} + +- (IBAction)closeMessageSheet:(id)sender +{ + [self cancelSheet:sender]; +} + +@end diff --git a/PBCommitMessageView.h b/Classes/Views/PBCommitMessageView.h similarity index 52% rename from PBCommitMessageView.h rename to Classes/Views/PBCommitMessageView.h index 0dbe073cf..7da89650b 100644 --- a/PBCommitMessageView.h +++ b/Classes/Views/PBCommitMessageView.h @@ -6,10 +6,12 @@ // Copyright 2008 Jeff Mesnil (http://jmesnil.net/). All rights reserved. // -#import +#import "GitXTextView.h" -@interface PBCommitMessageView : NSTextView { +@class PBGitRepository; -} +@interface PBCommitMessageView : GitXTextView + +@property (nonatomic, weak) PBGitRepository *repository; @end diff --git a/Classes/Views/PBCommitMessageView.m b/Classes/Views/PBCommitMessageView.m new file mode 100644 index 000000000..c2e7e2b19 --- /dev/null +++ b/Classes/Views/PBCommitMessageView.m @@ -0,0 +1,114 @@ +// +// PBCommitMessageView.m +// GitX +// +// Created by Jeff Mesnil on 13/10/08. +// Copyright 2008 Jeff Mesnil (http://jmesnil.net/). All rights reserved. +// + +#import "PBCommitMessageView.h" + +#import "PBGitDefaults.h" +#import "PBGitRepository.h" + +@implementation PBCommitMessageView + +- (void) awakeFromNib +{ + [super awakeFromNib]; + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + [defaults addObserver:self + forKeyPath:@"PBCommitMessageViewHasVerticalLine" + options:NSKeyValueObservingOptionNew + context:NULL]; + + [defaults addObserver:self + forKeyPath:@"PBCommitMessageViewVerticalLineLength" + options:NSKeyValueObservingOptionNew + context:NULL]; + + [defaults addObserver:self + forKeyPath:@"PBCommitMessageViewVerticalBodyLineLength" + options:NSKeyValueObservingOptionNew + context:NULL]; + + self.font = [NSFont fontWithName:@"SF Mono" size:12.0] ?: [NSFont fontWithName:@"Menlo" size:12.0]; +} + +- (void)dealloc +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + [defaults removeObserver:self forKeyPath:@"PBCommitMessageViewHasVerticalLine"]; + [defaults removeObserver:self forKeyPath:@"PBCommitMessageViewVerticalLineLength"]; + [defaults removeObserver:self forKeyPath:@"PBCommitMessageViewVerticalBodyLineLength"]; +} + +-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + [self setNeedsDisplay:YES]; +} + +- (void)drawRect:(NSRect)aRect +{ + [super drawRect:aRect]; + + if ([PBGitDefaults commitMessageViewHasVerticalLine]) { + + CGFloat characterWidth = [@" " sizeWithAttributes:@{NSFontAttributeName: self.font}].width; + CGFloat lineWidth = characterWidth * [PBGitDefaults commitMessageViewVerticalLineLength]; + NSRect line; + CGFloat padding; + CGFloat textViewHeight = [self bounds].size.height; + + // draw a vertical line after the given size (used as an indicator + // for the first line of the commit message) + [[NSColor lightGrayColor] set]; + padding = [[self textContainer] lineFragmentPadding]; + line.origin.x = padding + lineWidth; + line.origin.y = 0; + line.size.width = 1; + line.size.height = textViewHeight; + NSRectFill(line); + + // and one for the body of the commit message + lineWidth = characterWidth * [PBGitDefaults commitMessageViewVerticalBodyLineLength]; + [[NSColor darkGrayColor] set]; + padding = [[self textContainer] lineFragmentPadding]; + line.origin.x = padding + lineWidth; + line.origin.y = 0; + line.size.width = 1; + line.size.height = textViewHeight; + NSRectFill(line); + } +} + +- (BOOL)performDragOperation:(id )sender +{ + NSPasteboard *pboard = [sender draggingPasteboard]; + + if ( [[pboard types] containsObject:NSFilenamesPboardType] ) { + NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType]; + NSString *baseDir = [self.repository.workingDirectory stringByAppendingString:@"/"]; + if (baseDir) { + NSMutableArray *relativeNames = [NSMutableArray new]; + for (NSString *filename in filenames) { + if ([filename hasPrefix:baseDir]) { + NSString *relativeName = [filename substringFromIndex:(baseDir.length)]; + if (relativeName.length) { + [relativeNames addObject:relativeName]; + continue; + } + } + [relativeNames addObject:filename]; + } + [pboard clearContents]; + [pboard writeObjects:relativeNames]; + } + } + return [super performDragOperation:sender]; +} + +@end diff --git a/Classes/Views/PBCreateBranchSheet.h b/Classes/Views/PBCreateBranchSheet.h new file mode 100644 index 000000000..c3cc83425 --- /dev/null +++ b/Classes/Views/PBCreateBranchSheet.h @@ -0,0 +1,35 @@ +// +// PBCreateBranchSheet.h +// GitX +// +// Created by Nathan Kinsinger on 12/13/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import + +#import "RJModalRepoSheet.h" + +@protocol PBGitRefish; +@class PBGitRef; +@class PBGitRepositoryDocument; + +NS_ASSUME_NONNULL_BEGIN + +@interface PBCreateBranchSheet : RJModalRepoSheet + ++ (void)beginSheetWithRefish:(id )ref windowController:(PBGitWindowController *)windowController completionHandler:(nullable RJSheetCompletionHandler)handler; + +- (IBAction) createBranch:(nullable id)sender; +- (IBAction) closeCreateBranchSheet:(nullable id)sender; + +@property (nonatomic, strong) id startRefish; +@property (nonatomic, strong) PBGitRef *selectedRef; +@property (nonatomic, assign) BOOL shouldCheckoutBranch; + +@property (nonatomic, assign) IBOutlet NSTextField *branchNameField; +@property (nonatomic, assign) IBOutlet NSTextField *errorMessageField; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/Views/PBCreateBranchSheet.m b/Classes/Views/PBCreateBranchSheet.m new file mode 100644 index 000000000..fb10b7b41 --- /dev/null +++ b/Classes/Views/PBCreateBranchSheet.m @@ -0,0 +1,96 @@ +// +// PBCreateBranchSheet.m +// GitX +// +// Created by Nathan Kinsinger on 12/13/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import "PBCreateBranchSheet.h" +#import "PBGitRepository.h" +#import "PBGitDefaults.h" +#import "PBGitCommit.h" +#import "PBGitRef.h" +#import "PBGitWindowController.h" +#import "PBGitRepositoryDocument.h" + +@implementation PBCreateBranchSheet + +#pragma mark - +#pragma mark PBCreateBranchSheet + ++ (void)beginSheetWithRefish:(id )ref windowController:(PBGitWindowController *)windowController +{ + [self beginSheetWithRefish:ref windowController:windowController completionHandler:nil]; +} + ++ (void)beginSheetWithRefish:(id )ref windowController:(PBGitWindowController *)windowController completionHandler:(RJSheetCompletionHandler)handler { + PBCreateBranchSheet *sheet = [[self alloc] initWithWindowController:windowController atRefish:ref]; + [sheet beginCreateBranchSheetAtRefish:ref completionHandler:handler]; +} + + + +- (id)initWithWindowController:(PBGitWindowController *)windowController atRefish:(id)ref +{ + NSParameterAssert(windowController != nil); + NSParameterAssert(ref != nil); + + self = [super initWithWindowNibName:@"PBCreateBranchSheet" windowController:windowController]; + if (!self) + return nil; + + self.startRefish = ref; + + return self; +} + +- (void) beginCreateBranchSheetAtRefish:(id )ref completionHandler:(RJSheetCompletionHandler)handler +{ + [self window]; // loads the window (if it wasn't already) + [self.errorMessageField setStringValue:@""]; + self.shouldCheckoutBranch = [PBGitDefaults shouldCheckoutBranch]; + + // when creating a local branch tracking a remote branch preset the branch name to the name of the remote branch + if ([self.startRefish refishType] == kGitXRemoteBranchType) { + NSMutableArray *components = [[[self.startRefish shortName] componentsSeparatedByString:@"/"] mutableCopy]; + if ([components count] > 1) { + [components removeObjectAtIndex:0]; + NSString *branchName = [components componentsJoinedByString:@"/"]; + [self.branchNameField setStringValue:branchName]; + } + } + [self beginSheetWithCompletionHandler:handler]; +} + + + +#pragma mark IBActions + +- (IBAction) createBranch:(id)sender +{ + NSString *name = [self.branchNameField stringValue]; + self.selectedRef = [PBGitRef refFromString:[kGitXBranchRefPrefix stringByAppendingString:name]]; + + if (![self.repository checkRefFormat:[self.selectedRef ref]]) { + [self.errorMessageField setStringValue:NSLocalizedString(@"Invalid name", @"Error message for create branch command when the entered name cannot be used as a branch name")]; + [self.errorMessageField setHidden:NO]; + return; + } + + if ([self.repository refExists:self.selectedRef]) { + [self.errorMessageField setStringValue:NSLocalizedString(@"Branch already exists", @"Error message for create branch command")]; + [self.errorMessageField setHidden:NO]; + return; + } + + [self acceptSheet:sender]; +} + + +- (IBAction) closeCreateBranchSheet:(id)sender +{ + [self cancelSheet:sender]; +} + +@end diff --git a/Classes/Views/PBCreateTagSheet.h b/Classes/Views/PBCreateTagSheet.h new file mode 100644 index 000000000..0cab1bb50 --- /dev/null +++ b/Classes/Views/PBCreateTagSheet.h @@ -0,0 +1,30 @@ +// +// PBCreateTagSheet.h +// GitX +// +// Created by Nathan Kinsinger on 12/18/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import + +#import "PBGitRefish.h" +#import "RJModalRepoSheet.h" + +@class PBGitRepository; + + +@interface PBCreateTagSheet : RJModalRepoSheet + ++ (void) beginSheetWithRefish:(id )refish windowController:(PBGitWindowController *)windowController completionHandler:(RJSheetCompletionHandler)handler; + +- (IBAction) createTag:(id)sender; +- (IBAction) closeCreateTagSheet:(id)sender; + +@property (nonatomic, strong) id targetRefish; + +@property (nonatomic, weak) IBOutlet NSTextField *tagNameField; +@property (nonatomic, strong) IBOutlet NSTextView *tagMessageText; +@property (nonatomic, weak) IBOutlet NSTextField *errorMessageField; + +@end diff --git a/Classes/Views/PBCreateTagSheet.m b/Classes/Views/PBCreateTagSheet.m new file mode 100644 index 000000000..51e3c6321 --- /dev/null +++ b/Classes/Views/PBCreateTagSheet.m @@ -0,0 +1,74 @@ +// +// PBCreateTagSheet.m +// GitX +// +// Created by Nathan Kinsinger on 12/18/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import "PBCreateTagSheet.h" +#import "PBGitRepository.h" +#import "PBGitCommit.h" +#import "PBGitRef.h" +#import "PBGitWindowController.h" +#import "PBGitRepositoryDocument.h" +#import "PBGitRevSpecifier.h" + +@implementation PBCreateTagSheet + +#pragma mark - +#pragma mark PBCreateTagSheet + ++ (void) beginSheetWithRefish:(id )refish windowController:(PBGitWindowController *)windowController completionHandler:(RJSheetCompletionHandler)handler +{ + PBCreateTagSheet *sheet = [[self alloc] initWithWindowNibName:@"PBCreateTagSheet" windowController:windowController]; + [sheet beginCreateTagSheetAtRefish:refish completionHandler:handler]; +} + + +- (void) beginCreateTagSheetAtRefish:(id )refish completionHandler:(RJSheetCompletionHandler)handler +{ + self.targetRefish = refish; + + [self window]; + [self.errorMessageField setStringValue:@""]; + + [self beginSheetWithCompletionHandler:handler]; +} + + + +#pragma mark IBActions + +- (IBAction) createTag:(id)sender +{ + NSString *tagName = [self.tagNameField stringValue]; + [self.errorMessageField setHidden:YES]; + + NSString *refName = [@"refs/tags/" stringByAppendingString:tagName]; + if (![self.repository checkRefFormat:refName]) { + [self.errorMessageField setStringValue:NSLocalizedString(@"Invalid name", @"Error message for create tag command when the entered name cannot be used as a tag name")]; + [self.errorMessageField setHidden:NO]; + return; + } + + for (PBGitRevSpecifier *rev in self.repository.branches) { + NSString *name = [[rev ref] tagName]; + if ([tagName isEqualToString:name]) { + [self.errorMessageField setStringValue:NSLocalizedString(@"Tag already exists", @"Error message for create tag command when the entered tag name already exists")]; + [self.errorMessageField setHidden:NO]; + return; + } + } + [self acceptSheet:sender]; +} + + +- (IBAction) closeCreateTagSheet:(id)sender +{ + [self cancelSheet:sender]; +} + + + +@end diff --git a/PBFileChangesTableView.h b/Classes/Views/PBFileChangesTableView.h similarity index 76% rename from PBFileChangesTableView.h rename to Classes/Views/PBFileChangesTableView.h index 8dd6ba5af..6a8025280 100644 --- a/PBFileChangesTableView.h +++ b/Classes/Views/PBFileChangesTableView.h @@ -9,7 +9,5 @@ #import -@interface PBFileChangesTableView : NSTableView { -} - +@interface PBFileChangesTableView : NSTableView @end diff --git a/PBFileChangesTableView.m b/Classes/Views/PBFileChangesTableView.m similarity index 54% rename from PBFileChangesTableView.m rename to Classes/Views/PBFileChangesTableView.m index f2ea75d04..85b15bf49 100644 --- a/PBFileChangesTableView.m +++ b/Classes/Views/PBFileChangesTableView.m @@ -7,26 +7,34 @@ // #import "PBFileChangesTableView.h" -#import "PBGitIndexController.h" +#import "PBGitCommitController.h" @implementation PBFileChangesTableView #pragma mark NSTableView overrides + - (NSMenu *)menuForEvent:(NSEvent *)theEvent { if ([self delegate]) { - NSPoint eventLocation = [self convertPoint: [theEvent locationInWindow] fromView: nil]; + NSPoint eventLocation = [self convertPoint:[theEvent locationInWindow] fromView: nil]; NSInteger rowIndex = [self rowAtPoint:eventLocation]; - [self selectRowIndexes:[NSIndexSet indexSetWithIndex:rowIndex] byExtendingSelection:TRUE]; - return [[self delegate] menuForTable: self]; + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:rowIndex] byExtendingSelection:YES]; + return [super menuForEvent:theEvent]; } return nil; } -- (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL) local +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { return NSDragOperationEvery; } +#pragma mark NSView overrides + +-(BOOL)acceptsFirstResponder +{ + return [self numberOfRows] > 0; +} + @end diff --git a/Classes/Views/PBGitGradientBarView.h b/Classes/Views/PBGitGradientBarView.h new file mode 100644 index 000000000..7a500a2d6 --- /dev/null +++ b/Classes/Views/PBGitGradientBarView.h @@ -0,0 +1,19 @@ +// +// PBGitGradientBarView.h +// GitX +// +// Created by Nathan Kinsinger on 2/22/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@interface PBGitGradientBarView : NSView { + NSGradient *gradient; +} + +- (void) setTopShade:(float)topShade bottomShade:(float)bottomShade; +- (void) setTopColor:(NSColor *)topShade bottomColor:(NSColor *)bottomColor; + +@end diff --git a/Classes/Views/PBGitGradientBarView.m b/Classes/Views/PBGitGradientBarView.m new file mode 100644 index 000000000..5f4831710 --- /dev/null +++ b/Classes/Views/PBGitGradientBarView.m @@ -0,0 +1,52 @@ +// +// PBGitGradientBarView.m +// GitX +// +// Created by Nathan Kinsinger on 2/22/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitGradientBarView.h" + + + +@implementation PBGitGradientBarView + + +- (id) initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + [self setTopShade:1.0 bottomShade:0.0]; + + return self; +} + + +- (void) drawRect:(NSRect)dirtyRect +{ + [gradient drawInRect:[self bounds] angle:90]; +} + + +- (void) setTopColor:(NSColor *)topColor bottomColor:(NSColor *)bottomColor +{ + if (!topColor || !bottomColor) + return; + + gradient = [[NSGradient alloc] initWithStartingColor:bottomColor endingColor:topColor]; + [self setNeedsDisplay:YES]; +} + + +- (void) setTopShade:(float)topShade bottomShade:(float)bottomShade +{ + NSColor *topColor = [NSColor colorWithCalibratedWhite:topShade alpha:1.0]; + NSColor *bottomColor = [NSColor colorWithCalibratedWhite:bottomShade alpha:1.0]; + [self setTopColor:topColor bottomColor:bottomColor]; +} + + +@end diff --git a/PBGitRevisionCell.h b/Classes/Views/PBGitRevisionCell.h similarity index 73% rename from PBGitRevisionCell.h rename to Classes/Views/PBGitRevisionCell.h index d6aa4ed32..0dfa4b04c 100644 --- a/PBGitRevisionCell.h +++ b/Classes/Views/PBGitRevisionCell.h @@ -16,13 +16,13 @@ PBGitCommit *objectValue; PBGraphCellInfo *cellInfo; NSTextFieldCell *textCell; - IBOutlet PBGitHistoryController *controller; - IBOutlet id contextMenuDelegate; + __weak IBOutlet PBGitHistoryController *controller; + __weak IBOutlet id contextMenuDelegate; } -- (int) indexAtX:(float)x; +- (int) indexAtX:(CGFloat)x; - (NSRect) rectAtIndex:(int)index; - (void) drawLabelAtIndex:(int)index inRect:(NSRect)rect; -@property(retain) PBGitCommit* objectValue; +@property (copy) PBGitCommit* objectValue; @end diff --git a/Classes/Views/PBGitRevisionCell.m b/Classes/Views/PBGitRevisionCell.m new file mode 100644 index 000000000..7bca4bdb9 --- /dev/null +++ b/Classes/Views/PBGitRevisionCell.m @@ -0,0 +1,423 @@ +// +// PBGitRevisionCell.m +// GitX +// +// Created by Pieter de Bie on 17-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitRevisionCell.h" +#import "PBGitRef.h" +#import "PBGitCommit.h" +#import "PBGitRevSpecifier.h" +#import "GitXTextFieldCell.h" + +#import "NSColor+RGB.h" + +const int COLUMN_WIDTH = 10; +const BOOL ENABLE_SHADOW = NO; +const BOOL SHUFFLE_COLORS = NO; + +@implementation PBGitRevisionCell + +- (id) initWithCoder: (id) coder +{ + self = [super initWithCoder:coder]; + textCell = [[GitXTextFieldCell alloc] initWithCoder:coder]; + return self; +} + ++ (NSArray *)laneColors +{ + static const size_t colorCount = 8; + static NSArray *laneColors = nil; + if (!laneColors) { + float segment = 1.0f / colorCount; + NSMutableArray *colors = [NSMutableArray new]; + for (size_t i = 0; i < colorCount; ++i) { + NSColor *newColor = [NSColor colorWithCalibratedHue:(segment * i) saturation:0.7f brightness:0.8f alpha:1.0f]; + [colors addObject:newColor]; + } + if (SHUFFLE_COLORS) { + NSMutableArray *shuffledColors = [NSMutableArray new]; + while (colors.count) { + uint32_t index = arc4random_uniform(colors.count); + [shuffledColors addObject:colors[index]]; + [colors removeObjectAtIndex:index]; + } + colors = shuffledColors; + } + laneColors = [NSArray arrayWithArray:colors]; + } + + return laneColors; +} + ++ (NSColor *)shadowColor +{ + static NSColor *shadowColor = nil; + if (!shadowColor) { + uint8_t l = 64; + shadowColor = [NSColor colorWithR:l G:l B:l]; + } + return shadowColor; +} ++ (NSColor *)lineShadowColor +{ + static NSColor *shadowColor = nil; + if (!shadowColor) { + uint8_t l = 200; + shadowColor = [NSColor colorWithR:l G:l B:l]; + } + return shadowColor; +} + +- (void) drawLineFromColumn: (int) from toColumn: (int) to inRect: (NSRect) r offset: (int) offset color: (int) c +{ + NSPoint origin = r.origin; + + NSPoint source = NSMakePoint(origin.x + COLUMN_WIDTH * from, origin.y + offset); + NSPoint center = NSMakePoint( origin.x + COLUMN_WIDTH * to, origin.y + r.size.height * 0.5 + 0.5); + + if (ENABLE_SHADOW) + { + [NSGraphicsContext saveGraphicsState]; + + NSShadow *shadow = [NSShadow new]; + [shadow setShadowColor:[[self class] lineShadowColor]]; + [shadow setShadowOffset:NSMakeSize(0.5f, -0.5f)]; + [shadow set]; + } + NSArray* colors = [PBGitRevisionCell laneColors]; + [(NSColor*)[colors objectAtIndex: (c % [colors count])] set]; + + NSBezierPath * path = [NSBezierPath bezierPath]; + [path setLineWidth:2]; + [path setLineCapStyle:NSRoundLineCapStyle]; + [path moveToPoint: source]; + [path lineToPoint: center]; + [path stroke]; + + if (ENABLE_SHADOW) { + [NSGraphicsContext restoreGraphicsState]; + } +} + +- (BOOL) isCurrentCommit +{ + GTOID *thisOID = self.objectValue.OID; + + PBGitRepository* repository = [self.objectValue repository]; + GTOID *currentOID = [repository headOID]; + + return [currentOID isEqual:thisOID]; +} + +static BOOL isDarkMode() { + return [[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"] isEqualToString:@"Dark"]; +} + +- (void) drawCircleInRect: (NSRect) r +{ + long c = cellInfo.position; + NSPoint origin = r.origin; + NSPoint columnOrigin = { origin.x + COLUMN_WIDTH * c, origin.y}; + + NSRect oval = { columnOrigin.x - 5, columnOrigin.y + r.size.height * 0.5 - 5, 10, 10}; + + NSBezierPath * path = [NSBezierPath bezierPathWithOvalInRect:oval]; + if (ENABLE_SHADOW && false) { + [NSGraphicsContext saveGraphicsState]; + NSShadow *shadow = [NSShadow new]; + [shadow setShadowColor:[[self class] shadowColor]]; + [shadow setShadowOffset:NSMakeSize(0.5f, -0.5f)]; + [shadow setShadowBlurRadius:2.0f]; + [shadow set]; + } + if (isDarkMode()) { + [[NSColor whiteColor] set]; + } else { + [[NSColor blackColor] set]; + } + [path fill]; + if (ENABLE_SHADOW && false) { + [NSGraphicsContext restoreGraphicsState]; + } + + CGFloat outlineWidth = 1.4f; + NSRect smallOval = CGRectInset(oval, outlineWidth, outlineWidth); + + if ( [self isCurrentCommit ] ) { + [[NSColor colorWithCalibratedRed: 0Xfc/256.0 green:0Xa6/256.0 blue: 0X4f/256.0 alpha: 1.0] set]; + } else if (isDarkMode()) { + [[NSColor blackColor] set]; + } else { + [[NSColor whiteColor] set]; + } + + NSBezierPath *smallPath = [NSBezierPath bezierPathWithOvalInRect:smallOval]; + [smallPath fill]; + +} + +- (void) drawTriangleInRect: (NSRect) r sign: (char) sign +{ + long c = cellInfo.position; + int columnHeight = 10; + int columnWidth = 8; + + NSPoint top; + if (sign == '<') + top.x = round(r.origin.x) + 10 * c + 4; + else { + top.x = round(r.origin.x) + 10 * c - 4; + columnWidth *= -1; + } + top.y = r.origin.y + (r.size.height - columnHeight) / 2; + + NSBezierPath * path = [NSBezierPath bezierPath]; + // Start at top + [path moveToPoint: NSMakePoint(top.x, top.y)]; + // Go down + [path lineToPoint: NSMakePoint(top.x, top.y + columnHeight)]; + // Go left top + [path lineToPoint: NSMakePoint(top.x - columnWidth, top.y + columnHeight / 2)]; + // Go to top again + [path closePath]; + + [[NSColor whiteColor] set]; + [path fill]; + [[NSColor blackColor] set]; + [path setLineWidth: 2]; + [path stroke]; +} + +- (NSMutableDictionary*) attributesForRefLabelSelected: (BOOL) selected +{ + NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithCapacity:2]; + NSMutableParagraphStyle* style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + + [style setAlignment:NSCenterTextAlignment]; + [attributes setObject:style forKey:NSParagraphStyleAttributeName]; + + [attributes setObject:[NSFont systemFontOfSize:10] forKey:NSFontAttributeName]; + + NSShadow *shadow = nil; + + if (shadow) { + attributes[NSShadowAttributeName] = shadow; + } + + return attributes; +} + +- (NSColor*) colorForRef: (PBGitRef*) ref +{ + BOOL isHEAD = [ref.ref isEqualToString:[[[controller repository] headRef] simpleRef]]; + + if (isHEAD) { + return [NSColor colorWithCalibratedRed: 0Xfc/256.0 green:0Xa6/256.0 blue: 0X4f/256.0 alpha: 1.0]; + } + + NSString* type = [ref type]; + if ([type isEqualToString:@"head"]) { + return [NSColor colorWithCalibratedRed: 0X9a/256.0 green:0Xe2/256.0 blue: 0X84/256.0 alpha: 1.0]; + } else if ([type isEqualToString:@"remote"]) { + return [NSColor colorWithCalibratedRed: 0xa2/256.0 green:0Xcf/256.0 blue: 0Xef/256.0 alpha: 1.0]; + } else if ([type isEqualToString:@"tag"]) { + return [NSColor colorWithCalibratedRed: 0Xfc/256.0 green:0Xed/256.0 blue: 0X6f/256.0 alpha: 1.0]; + } + + return [NSColor yellowColor]; +} + +-(NSArray *)rectsForRefsinRect:(NSRect) rect; +{ + NSMutableArray *array = [NSMutableArray array]; + + static const int ref_padding = 4; + static const int ref_spacing = 4; + + NSRect lastRect = rect; + lastRect.origin.x = round(lastRect.origin.x); + lastRect.origin.y = round(lastRect.origin.y); + + for (PBGitRef *ref in self.objectValue.refs) { + NSMutableDictionary* attributes = [self attributesForRefLabelSelected:NO]; + NSSize textSize = [[ref shortName] sizeWithAttributes:attributes]; + + NSRect newRect = lastRect; + newRect.size.width = textSize.width + ref_padding * 2; + newRect.size.height = textSize.height; + newRect.origin.y = rect.origin.y + (rect.size.height - newRect.size.height) / 2; + + if (NSContainsRect(rect, newRect)) { + [array addObject:[NSValue valueWithRect:newRect]]; + lastRect = newRect; + lastRect.origin.x += (int)lastRect.size.width + ref_spacing; + } + } + + return array; +} + +- (void) drawLabelAtIndex:(int)index inRect:(NSRect)rect +{ + NSArray *refs = self.objectValue.refs; + PBGitRef *ref = [refs objectAtIndex:index]; + + NSMutableDictionary* attributes = [self attributesForRefLabelSelected:[self isHighlighted]]; + NSBezierPath *border = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:2 yRadius:2]; + [[self colorForRef:ref] set]; + + + if (ENABLE_SHADOW) { + [NSGraphicsContext saveGraphicsState]; + + NSShadow *shadow = [NSShadow new]; + [shadow setShadowColor:[NSColor grayColor]];//[[self class] shadowColor]]; + [shadow setShadowOffset:NSMakeSize(0.5f, -0.5f)]; + [shadow setShadowBlurRadius:2.0f]; + [shadow set]; + } + [border fill]; + if (ENABLE_SHADOW) { + [NSGraphicsContext restoreGraphicsState]; + } +// [[NSColor blackColor] set]; +// [border stroke]; + [[ref shortName] drawInRect:rect withAttributes:attributes]; +} + +- (void) drawRefsInRect:(NSRect *)refRect +{ + [[NSColor blackColor] setStroke]; + + NSRect lastRect = NSMakeRect(0, 0, 0, 0); + int index = 0; + for (NSValue *rectValue in [self rectsForRefsinRect:*refRect]) + { + NSRect rect = [rectValue rectValue]; + [self drawLabelAtIndex:index inRect:rect]; + lastRect = rect; + ++index; + } + + // Only update rect to account for drawn refs if necessary to push + // subsequent content to the right. + if (index > 0) { + const CGFloat PADDING = 4; + refRect->size.width -= lastRect.origin.x - refRect->origin.x + lastRect.size.width - PADDING; + refRect->origin.x = lastRect.origin.x + lastRect.size.width + PADDING; + } +} + +- (void) drawWithFrame:(NSRect)rect inView:(NSView *)view +{ + cellInfo = [self.objectValue lineInfo]; + + if (cellInfo && ![controller hasNonlinearPath]) { + float pathWidth = 10 + COLUMN_WIDTH * cellInfo.numColumns; + + NSRect ownRect; + NSDivideRect(rect, &ownRect, &rect, pathWidth, NSMinXEdge); + + int i; + struct PBGitGraphLine *lines = cellInfo.lines; + for (i = 0; i < cellInfo.nLines; i++) { + if (lines[i].upper == 0) + [self drawLineFromColumn: lines[i].from toColumn: lines[i].to inRect:ownRect offset: (int)ownRect.size.height color: lines[i].colorIndex]; + else + [self drawLineFromColumn: lines[i].from toColumn: lines[i].to inRect:ownRect offset: 0 color:lines[i].colorIndex]; + } + + if (cellInfo.sign == '<' || cellInfo.sign == '>') + [self drawTriangleInRect: ownRect sign: cellInfo.sign]; + else + [self drawCircleInRect: ownRect]; + } + + + if ([self.objectValue refs] && [[self.objectValue refs] count]) + [self drawRefsInRect:&rect]; + + // Still use this superclass because of hilighting differences + //_contents = [self.objectValue subject]; + //[super drawWithFrame:rect inView:view]; + [textCell setObjectValue: [self.objectValue subject]]; + [textCell setHighlighted: [self isHighlighted]]; + [textCell drawWithFrame:rect inView: view]; +} + +- (void) setObjectValue: (PBGitCommit*)object { + [super setObjectValue:[NSValue valueWithNonretainedObject:object]]; +} + +- (PBGitCommit*) objectValue { + return [[super objectValue] nonretainedObjectValue]; +} + +- (int) indexAtX:(CGFloat)x +{ + cellInfo = [self.objectValue lineInfo]; + float pathWidth = 0; + if (cellInfo && ![controller hasNonlinearPath]) + pathWidth = 10 + 10 * cellInfo.numColumns; + + int index = 0; + NSRect refRect = NSMakeRect(pathWidth, 0, 1000, 10000); + for (NSValue *rectValue in [self rectsForRefsinRect:refRect]) + { + NSRect rect = [rectValue rectValue]; + if (x >= rect.origin.x && x <= (rect.origin.x + rect.size.width)) + return index; + ++index; + } + + return -1; +} + +- (NSRect) rectAtIndex:(int)index +{ + cellInfo = [self.objectValue lineInfo]; + float pathWidth = 0; + if (cellInfo && ![controller hasNonlinearPath]) + pathWidth = 10 + 10 * cellInfo.numColumns; + NSRect refRect = NSMakeRect(pathWidth, 0, 1000, 10000); + + return [[[self rectsForRefsinRect:refRect] objectAtIndex:index] rectValue]; +} + +# pragma mark context menu delegate methods + +- (NSMenu *) menuForEvent:(NSEvent *)event inRect:(NSRect)rect ofView:(NSView *)view +{ + if (!contextMenuDelegate) + return [self menu]; + + PBGitRef *clickedRef = [self findClickedRefFor:event rect:rect ofView:view]; + + NSArray *items = nil; + if (clickedRef) + items = [contextMenuDelegate menuItemsForRef:clickedRef]; + else { + NSArray *relevantCommits = [controller.selectedCommits containsObject:self.objectValue] + ? controller.selectedCommits + : @[self.objectValue]; + items = [contextMenuDelegate menuItemsForCommits:relevantCommits]; + } + + NSMenu *menu = [[NSMenu alloc] init]; + [menu setAutoenablesItems:NO]; + for (NSMenuItem *item in items) + [menu addItem:item]; + return menu; +} + +- (PBGitRef *) findClickedRefFor:(NSEvent *)event rect:(NSRect)rect ofView:(NSView *)view +{ + int i = [self indexAtX:[view convertPoint:[event locationInWindow] fromView:nil].x - rect.origin.x]; + return (i >= 0) ? [self.objectValue.refs objectAtIndex:i] : nil; +} + +@end diff --git a/Classes/Views/PBGitXMessageSheet.h b/Classes/Views/PBGitXMessageSheet.h new file mode 100644 index 000000000..68dcc1cdb --- /dev/null +++ b/Classes/Views/PBGitXMessageSheet.h @@ -0,0 +1,49 @@ +// +// PBGitXMessageSheet.h +// GitX +// +// Created by BrotherBard on 7/4/10. +// Copyright 2010 BrotherBard. All rights reserved. +// + +#import + +#import "RJModalRepoSheet.h" + +@interface PBGitXMessageSheet : RJModalRepoSheet +{ + NSImageView *iconView; + NSTextField *messageField; + NSTextView *infoView; + NSScrollView *scrollView; +} + ++ (void)beginSheetWithMessage:(NSString *)message + info:(NSString *)info + windowController:(PBGitWindowController *)windowController; + ++ (void)beginSheetWithError:(NSError *)error + windowController:(PBGitWindowController *)windowController; + ++ (void)beginSheetWithMessage:(NSString *)message + info:(NSString *)info + windowController:(PBGitWindowController *)windowController + completionHandler:(RJSheetCompletionHandler)handler; + ++ (void)beginSheetWithError:(NSError *)error + windowController:(PBGitWindowController *)windowController + completionHandler:(RJSheetCompletionHandler)handler; + +- (void)beginMessageSheetWithMessageText:(NSString *)message + infoText:(NSString *)info + completionHandler:(RJSheetCompletionHandler)handler; + +- (IBAction)closeMessageSheet:(id)sender; + + +@property IBOutlet NSImageView *iconView; +@property IBOutlet NSTextField *messageField; +@property IBOutlet NSTextView *infoView; +@property IBOutlet NSScrollView *scrollView; + +@end diff --git a/Classes/Views/PBGitXMessageSheet.m b/Classes/Views/PBGitXMessageSheet.m new file mode 100644 index 000000000..8a9389028 --- /dev/null +++ b/Classes/Views/PBGitXMessageSheet.m @@ -0,0 +1,177 @@ +// +// PBGitXMessageSheet.m +// GitX +// +// Created by BrotherBard on 7/4/10. +// Copyright 2010 BrotherBard. All rights reserved. +// + +#import "PBGitXMessageSheet.h" +#import "PBTask.h" + + +#define MaxScrollViewHeight 125.0f + + +@interface PBGitXMessageSheet () + +- (void)setInfoString:(NSString *)info; +- (void)resizeWindow; + +@end + +@implementation PBGitXMessageSheet + +@synthesize iconView; +@synthesize messageField; +@synthesize infoView; +@synthesize scrollView; + + +#pragma mark - +#pragma mark PBGitXMessageSheet + + ++ (void)beginSheetWithMessage:(NSString *)message + info:(NSString *)info + windowController:(PBGitWindowController *)windowController +{ + [self beginSheetWithMessage:message + info:info + windowController:windowController + completionHandler:nil]; +} + ++ (void)beginSheetWithError:(NSError *)error + windowController:(PBGitWindowController *)windowController +{ + [self beginSheetWithError:error windowController:windowController completionHandler:nil]; +} + ++ (void)beginSheetWithMessage:(NSString *)message + info:(NSString *)info + windowController:(PBGitWindowController *)windowController + completionHandler:(RJSheetCompletionHandler)handler +{ + PBGitXMessageSheet *sheet = [[self alloc] initWithWindowNibName:@"PBGitXMessageSheet" windowController:windowController]; + [sheet beginMessageSheetWithMessageText:message + infoText:info + completionHandler:handler]; +} + ++ (void)beginSheetWithError:(NSError *)error + windowController:(PBGitWindowController *)windowController + completionHandler:(RJSheetCompletionHandler)handler +{ + PBGitXMessageSheet *sheet = [[self alloc] initWithWindowNibName:@"PBGitXMessageSheet" windowController:windowController]; + + NSMutableArray *messageParts = [NSMutableArray array]; + + if (error.localizedFailureReason) + [messageParts addObject:error.localizedFailureReason]; + + if (error.localizedRecoverySuggestion) { + NSString *message = NSLocalizedString(@"Maybe you could try the following:", @"PBGitXMessageSheet - localized recovery suggestion header"); + [message stringByAppendingString:@"\n"]; + [message stringByAppendingString:error.localizedRecoverySuggestion]; + [messageParts addObject:message]; + } + + NSError *taskError = error.userInfo[NSUnderlyingErrorKey]; + if (taskError && taskError.domain == PBTaskErrorDomain) { + [messageParts addObject:NSLocalizedString(@"The underlying task failed:", @"PBGitXMessageSheet - task failed header")]; + if (taskError.code == PBTaskNonZeroExitCodeError) { + NSString *message = NSLocalizedString(@"Return code: %@", @"PBGitXMessageSheet - task return code header"); + message = [NSString stringWithFormat:message, taskError.userInfo[PBTaskTerminationStatusKey]]; + [messageParts addObject:message]; + message = NSLocalizedString(@"Output:", @"PBGitXMessageSheet - task output header"); + message = [message stringByAppendingString:@"\n"]; + message = [message stringByAppendingString:taskError.userInfo[PBTaskTerminationOutputKey]]; + [messageParts addObject:message]; + } else { + [messageParts addObject:taskError.localizedDescription]; + [messageParts addObject:taskError.localizedFailureReason]; + } + } + + NSString *infoText = [messageParts componentsJoinedByString:@"\n\n"]; + + [sheet beginMessageSheetWithMessageText:[error localizedDescription] + infoText:infoText + completionHandler:handler]; +} + +- (IBAction)closeMessageSheet:(id)sender +{ + [self acceptSheet:sender]; +} + + + +#pragma mark Private + +- (void)beginMessageSheetWithMessageText:(NSString *)message + infoText:(NSString *)info + completionHandler:(RJSheetCompletionHandler)handler; +{ + [self window]; + + [self.messageField setStringValue:message]; + [self setInfoString:info]; + [self resizeWindow]; + + [self beginSheetWithCompletionHandler:handler]; +} + + +- (void)setInfoString:(NSString *)info +{ + NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSFont labelFontOfSize:[NSFont smallSystemFontSize]] + forKey:NSFontAttributeName]; + NSAttributedString *attributedInfoString = [[NSAttributedString alloc] initWithString:info attributes:attributes]; + [[self.infoView textStorage] setAttributedString:attributedInfoString]; +} + + +- (void)resizeWindow +{ + // resize for message text + NSRect messageFrame = [self.messageField frame]; + NSSize boundingSize = messageFrame.size; + boundingSize.height = 0.0f; + NSAttributedString *attributedTitle = [self.messageField attributedStringValue]; + NSRect boundingRect = [attributedTitle boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin]; + CGFloat heightDelta = boundingRect.size.height - messageFrame.size.height; + if (heightDelta > 0.0f) { + messageFrame.size.height += heightDelta; + messageFrame.origin.y -= heightDelta; + [self.messageField setFrame:messageFrame]; + + NSRect scrollFrame = [self.scrollView frame]; + scrollFrame.size.height -= heightDelta; + [self.scrollView setFrame:scrollFrame]; + + NSRect windowFrame = [[self window] frame]; + windowFrame.size.height += heightDelta; + [[self window] setFrame:windowFrame display:NO]; + } + + // resize for info text + NSRect scrollFrame = [self.scrollView frame]; + boundingSize = [self.scrollView bounds].size; + boundingSize.height = 0.0f; + NSAttributedString *attributedInfo = [[self.infoView layoutManager] attributedString]; + boundingRect = [attributedInfo boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin]; + heightDelta = boundingRect.size.height - scrollFrame.size.height; + if (heightDelta > MaxScrollViewHeight) + heightDelta = MaxScrollViewHeight; + if (heightDelta > 0.0f) { + NSRect windowFrame = [[self window] frame]; + windowFrame.size.height += heightDelta; + [[self window] setFrame:windowFrame display:NO]; + } + [self.infoView scrollRangeToVisible:NSMakeRange(0, 0)]; +} + + +@end diff --git a/PBIconAndTextCell.h b/Classes/Views/PBIconAndTextCell.h similarity index 87% rename from PBIconAndTextCell.h rename to Classes/Views/PBIconAndTextCell.h index b30cf1387..18ea55aaa 100644 --- a/PBIconAndTextCell.h +++ b/Classes/Views/PBIconAndTextCell.h @@ -11,11 +11,10 @@ @interface PBIconAndTextCell : NSTextFieldCell { - NSImage *image; BOOL mouseDownInButton; BOOL mouseHoveredInButton; } -@property (retain) NSImage *image; +@property NSImage *image; - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView; - (NSSize)cellSize; diff --git a/PBIconAndTextCell.m b/Classes/Views/PBIconAndTextCell.m similarity index 70% rename from PBIconAndTextCell.m rename to Classes/Views/PBIconAndTextCell.m index c2c005206..eae6b45c7 100644 --- a/PBIconAndTextCell.m +++ b/Classes/Views/PBIconAndTextCell.m @@ -9,51 +9,60 @@ #import "PBIconAndTextCell.h" +static const CGFloat kIconSpacing = 4; +static const CGFloat kIconPadding = 3; + +@interface PBIconAndTextCell () + +@property(strong) NSImageCell *imageCell; + +@end @implementation PBIconAndTextCell -@synthesize image; +@dynamic image; +@synthesize imageCell; + +- (id)initWithCoder:(NSCoder *)decoder +{ + if ((self = [super initWithCoder:decoder])) { + imageCell = [[NSImageCell alloc] init]; + } + return self; +} + +- (NSImage *)image +{ + return imageCell.image; +} -- (void)dealloc +- (void)setImage:(NSImage *)image { - self.image = nil; - [super dealloc]; + imageCell.image = image; } - (id)copyWithZone:(NSZone *)zone { PBIconAndTextCell *cell = [super copyWithZone:zone]; - cell.image = image; + cell.imageCell = [imageCell copyWithZone:zone]; return cell; } - (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength { NSRect textFrame, imageFrame; - NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); + NSDivideRect (aRect, &imageFrame, &textFrame, kIconSpacing + [imageCell.image size].width, NSMinXEdge); [super selectWithFrame: textFrame inView: controlView editor:textObj delegate:anObject start:selStart length:selLength]; } - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { - if (image) { - NSSize imageSize; - NSRect imageFrame; - - imageSize = [image size]; - NSDivideRect(cellFrame, &imageFrame, &cellFrame, 3 + imageSize.width, NSMinXEdge); - if ([self drawsBackground]) { - [[self backgroundColor] set]; - NSRectFill(imageFrame); - } - imageFrame.origin.x += 3; - imageFrame.size = imageSize; - - if ([controlView isFlipped]) - imageFrame.origin.y += floor((cellFrame.size.height + imageFrame.size.height) / 2); - else - imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2); - - [image compositeToPoint:imageFrame.origin operation:NSCompositeSourceOver]; + if (imageCell.image) { + NSRect imageFrame = cellFrame; + imageFrame.size.width = imageCell.image.size.width; + imageFrame.origin.x += kIconPadding; + [imageCell drawWithFrame:imageFrame inView:controlView]; + cellFrame.origin.x += imageFrame.size.width + kIconSpacing; + cellFrame.size.width -= imageFrame.size.width + kIconSpacing; } [super drawWithFrame:cellFrame inView:controlView]; } @@ -61,7 +70,7 @@ - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView - (NSSize)cellSize { NSSize cellSize = [super cellSize]; - cellSize.width += (image ? [image size].width : 0) + 3; + cellSize.width += (imageCell.image ? [imageCell.image size].width : 0) + kIconSpacing; return cellSize; } @@ -70,12 +79,12 @@ - (NSSize)cellSize // =============== // Adopted from PhotoSearch Apple sample code -- (NSUInteger)hitTestForEvent:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView +- (NSCellHitResult)hitTestForEvent:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView { NSPoint point = [controlView convertPoint:[event locationInWindow] fromView:nil]; NSRect textFrame, imageFrame; - NSDivideRect (cellFrame, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); + NSDivideRect (cellFrame, &imageFrame, &textFrame, kIconSpacing + [imageCell.image size].width, NSMinXEdge); if (NSMouseInRect(point, imageFrame, [controlView isFlipped])) return NSCellHitContentArea | NSCellHitTrackableArea; @@ -93,7 +102,7 @@ - (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView * [self setControlView:controlView]; NSRect textFrame, imageFrame; - NSDivideRect (cellFrame, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge); + NSDivideRect (cellFrame, &imageFrame, &textFrame, kIconSpacing + [imageCell.image size].width, NSMinXEdge); while ([theEvent type] != NSLeftMouseUp) { // This is VERY simple event tracking. We simply check to see if the mouse is in the "i" button or not and dispatch entered/exited mouse events NSPoint point = [controlView convertPoint:[theEvent locationInWindow] fromView:nil]; diff --git a/PBQLOutlineView.h b/Classes/Views/PBQLOutlineView.h similarity index 82% rename from PBQLOutlineView.h rename to Classes/Views/PBQLOutlineView.h index d0f8284ba..762503f3a 100644 --- a/PBQLOutlineView.h +++ b/Classes/Views/PBQLOutlineView.h @@ -10,7 +10,7 @@ #import "PBGitHistoryController.h" @interface PBQLOutlineView : NSOutlineView { - IBOutlet PBGitHistoryController* controller; + __weak IBOutlet PBGitHistoryController* controller; } @end diff --git a/PBQLOutlineView.m b/Classes/Views/PBQLOutlineView.m similarity index 83% rename from PBQLOutlineView.m rename to Classes/Views/PBQLOutlineView.m index 459d90801..e73b975a8 100644 --- a/PBQLOutlineView.m +++ b/Classes/Views/PBQLOutlineView.m @@ -7,7 +7,10 @@ // #import "PBQLOutlineView.h" +#import "PBGitTree.h" +@interface PBQLOutlineView () +@end @implementation PBQLOutlineView @@ -19,16 +22,14 @@ @implementation PBQLOutlineView return a; } -/* Needed to drag outside application */ -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL) local -{ +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { return NSDragOperationCopy; } - (void) keyDown: (NSEvent *) event { if ([[event characters] isEqualToString:@" "]) { - [controller toggleQuickView:self]; + [controller toggleQLPreviewPanel:self]; return; } @@ -67,11 +68,13 @@ - (NSMenu *)menuForEvent:(NSEvent *)theEvent // select the row that was clicked before showing the menu for the event NSPoint mousePoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; - int row = [self rowAtPoint:mousePoint]; + NSInteger row = [self rowAtPoint:mousePoint]; // figure out if the row that was just clicked on is currently selected - if ([selectedRowIndexes containsIndex:row] == NO) - [self selectRow:row byExtendingSelection:NO]; + if ([selectedRowIndexes containsIndex:row] == NO) { + NSIndexSet *index = [NSIndexSet indexSetWithIndex:row]; + [self selectRowIndexes:index byExtendingSelection:NO]; + } } return [controller contextMenuForTreeView]; diff --git a/Classes/Views/PBQLTextView.h b/Classes/Views/PBQLTextView.h new file mode 100644 index 000000000..5b93b1751 --- /dev/null +++ b/Classes/Views/PBQLTextView.h @@ -0,0 +1,19 @@ +// +// PBQLTextView.h +// GitX +// +// Created by Nathan Kinsinger on 3/22/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@class PBGitHistoryController; + + +@interface PBQLTextView : NSTextView { + __weak IBOutlet PBGitHistoryController *controller; +} + +@end diff --git a/Classes/Views/PBQLTextView.m b/Classes/Views/PBQLTextView.m new file mode 100644 index 000000000..fccb3ad25 --- /dev/null +++ b/Classes/Views/PBQLTextView.m @@ -0,0 +1,25 @@ +// +// PBQLTextView.m +// GitX +// +// Created by Nathan Kinsinger on 3/22/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBQLTextView.h" +#import "PBGitHistoryController.h" + + +@implementation PBQLTextView + +- (void) keyDown: (NSEvent *) event +{ + if ([[event characters] isEqualToString:@" "]) { + [controller toggleQLPreviewPanel:self]; + return; + } + + [super keyDown:event]; +} + +@end diff --git a/Classes/Views/PBRefMenuItem.h b/Classes/Views/PBRefMenuItem.h new file mode 100644 index 000000000..329f59535 --- /dev/null +++ b/Classes/Views/PBRefMenuItem.h @@ -0,0 +1,18 @@ +// +// PBRefMenuItem.h +// GitX +// +// Created by Pieter de Bie on 01-11-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import +#import "PBGitRef.h" +#import "PBGitCommit.h" + +@interface NSMenuItem (PBRefMenuItem) + ++ (NSArray *)pb_defaultMenuItemsForRef:(PBGitRef *)refs inRepository:(PBGitRepository *)repo; ++ (NSArray *)pb_defaultMenuItemsForCommits:(NSArray *)commits; + +@end diff --git a/Classes/Views/PBRefMenuItem.m b/Classes/Views/PBRefMenuItem.m new file mode 100644 index 000000000..4143d18a2 --- /dev/null +++ b/Classes/Views/PBRefMenuItem.m @@ -0,0 +1,259 @@ +// +// PBRefMenuItem.m +// GitX +// +// Created by Pieter de Bie on 01-11-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import "PBRefMenuItem.h" +#import "PBGitRepository.h" +#import "PBGitRevSpecifier.h" + +/* + * TODO: This is kept for simplicity reasons. A "more correct" way of handling + * menus would be to have them in NIBs, and handle them using NSMenuValidation. + */ + +@implementation NSMenuItem (PBRefMenuItem) + ++ (NSMenuItem *)pb_itemWithTitle:(NSString *)title action:(SEL)selector enabled:(BOOL)isEnabled +{ + if (!isEnabled) + selector = nil; + + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:@""]; + [item setEnabled:isEnabled]; + return item; +} + ++ (NSArray *)pb_defaultMenuItemsForStashRef:(PBGitRef *)ref +{ + NSMutableArray *items = [NSMutableArray array]; + NSString *targetRefName = [ref shortName]; + BOOL isCleanWorkingCopy = YES; + + // pop + NSString *stashPopTitle = [NSString stringWithFormat:NSLocalizedString(@"Pop %@", @"Contextual Menu Item to pop the selected stash ref"), targetRefName]; + [items addObject:[NSMenuItem pb_itemWithTitle:stashPopTitle action:@selector(stashPop:) enabled:isCleanWorkingCopy]]; + + // apply + NSString *stashApplyTitle = [NSString stringWithFormat:NSLocalizedString(@"Apply %@", @"Contextual Menu Item to apply the selected stash ref"), targetRefName]; + [items addObject:[NSMenuItem pb_itemWithTitle:stashApplyTitle action:@selector(stashApply:) enabled:YES]]; + + // view diff + NSString *stashDiffTitle = @"View Diff"; + [items addObject:[NSMenuItem pb_itemWithTitle:stashDiffTitle action:@selector(stashViewDiff:) enabled:YES]]; + + [items addObject:[NSMenuItem separatorItem]]; + + // drop + NSString *stashDropTitle = [NSString stringWithFormat:NSLocalizedString(@"Drop %@", @"Contextual Menu Item to drop the selected stash ref"), targetRefName]; + [items addObject:[NSMenuItem pb_itemWithTitle:stashDropTitle action:@selector(stashDrop:) enabled:YES]]; + + for (NSMenuItem *item in items) { + if (!item.representedObject) { + item.representedObject = ref; + } + } + + return items; +} + + ++ (NSArray *)pb_defaultMenuItemsForRef:(PBGitRef *)ref inRepository:(PBGitRepository *)repo +{ + if (!ref || !repo) { + return nil; + } + + if (ref.isStash) { + return [self pb_defaultMenuItemsForStashRef:ref]; + } + + NSString *refName = ref.shortName; + + PBGitRef *headRef = repo.headRef.ref; + NSString *headRefName = headRef.shortName; + + BOOL isHead = [ref isEqualToRef:headRef]; + BOOL isOnHeadBranch = isHead ? YES : [repo isRefOnHeadBranch:ref]; + BOOL isDetachedHead = (isHead && [headRefName isEqualToString:@"HEAD"]); + + NSString *remoteName = ref.remoteName; + if (!remoteName && ref.isBranch) { + remoteName = [[repo remoteRefForBranch:ref error:NULL] remoteName]; + } + BOOL hasRemote = (remoteName ? YES : NO); + BOOL isRemote = (ref.isRemote && !ref.isRemoteBranch); + + NSMutableArray *items = [NSMutableArray array]; + if (!isRemote) { + // checkout ref + NSString *checkoutTitle = [NSString stringWithFormat:NSLocalizedString(@"Checkout “%@”", @"Contextual Menu Item to check out the selected ref"), refName]; + [items addObject:[NSMenuItem pb_itemWithTitle:checkoutTitle action:@selector(checkout:) enabled:!isHead]]; + [items addObject:[NSMenuItem separatorItem]]; + + // create branch + NSString *createBranchTitle = ref.isRemoteBranch + ? [NSString stringWithFormat:NSLocalizedString(@"Create Branch tracking “%@”…", @"Contextual Menu Item to create a branch tracking the selected remote branch"), refName] + : NSLocalizedString(@"Create Branch…", @"Contextual Menu Item to create a new branch at the selected ref"); + [items addObject:[NSMenuItem pb_itemWithTitle:createBranchTitle action:@selector(createBranch:) enabled:YES]]; + + // create tag + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Create Tag…", @"Contextual Menu Item to create a tag at the selected ref") action:@selector(createTag:) enabled:YES]]; + + // view tag info + if (ref.isTag) { + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"View Tag Info…", @"Contextual Menu Item to view Information about the selected tag") action:@selector(showTagInfoSheet:) enabled:YES]]; + } + + // Diff + NSString *diffTitle = [NSString stringWithFormat:NSLocalizedString(@"Diff with “%@”", @"Contextual Menu Item to view a diff between the selected ref and HEAD"), headRefName]; + [items addObject:[NSMenuItem pb_itemWithTitle:diffTitle action:@selector(diffWithHEAD:) enabled:!isHead]]; + [items addObject:[NSMenuItem separatorItem]]; + + // merge ref + NSString *mergeTitle = isOnHeadBranch + ? NSLocalizedString(@"Merge", @"Inactive Contextual Menu Item for merging") + : [NSString stringWithFormat:@"Merge %@ into %@", refName, headRefName]; + [items addObject:[NSMenuItem pb_itemWithTitle:mergeTitle action:@selector(merge:) enabled:!isOnHeadBranch]]; + + // rebase + NSString *rebaseTitle = isOnHeadBranch + ? NSLocalizedString(@"Rebase", @"Inactive Contextual Menu Item for rebasing") + : [NSString stringWithFormat:NSLocalizedString(@"Rebase ”%@“ onto “%@”", @"Contextual Menu Item to rebase HEAD onto the selected ref"), headRefName, refName]; + [items addObject:[NSMenuItem pb_itemWithTitle:rebaseTitle action:@selector(rebaseHeadBranch:) enabled:!isOnHeadBranch]]; + + [items addObject:[NSMenuItem separatorItem]]; + } + + // fetch + NSString *fetchTitle = hasRemote + ? [NSString stringWithFormat:NSLocalizedString(@"Fetch “%@”", @"Contextual Menu Item to fetch the selected remote"), remoteName] + : NSLocalizedString(@"Fetch", @"Inactive Contextual Menu Item for fetching"); + [items addObject:[NSMenuItem pb_itemWithTitle:fetchTitle action:@selector(fetchRemote:) enabled:hasRemote]]; + + // pull + NSString *pullTitle = hasRemote + ? [NSString stringWithFormat:NSLocalizedString(@"Pull “%@” and Update “%@”", @"Contextual Menu Item to pull the remote and update the selected branch"), remoteName, headRefName] + : NSLocalizedString(@"Pull", @"Inactive Contextual Menu Item for pulling"); + [items addObject:[NSMenuItem pb_itemWithTitle:pullTitle action:@selector(pullRemote:) enabled:hasRemote]]; + + // push + if (isRemote || ref.isRemoteBranch) { + // push updates to remote + NSString *pushTitle = [NSString stringWithFormat:NSLocalizedString(@"Push Updates to “%@”", @"Contextual Menu Item to push updates of the selected ref to he named remote"), remoteName]; + [items addObject:[NSMenuItem pb_itemWithTitle:pushTitle action:@selector(pushUpdatesToRemote:) enabled:YES]]; + } + else if (isDetachedHead) { + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Push", @"Inactive Contextual Menu Item for pushing") action:nil enabled:NO]]; + } + else { + // push to default remote + BOOL hasDefaultRemote = NO; + if (!ref.isTag && hasRemote) { + hasDefaultRemote = YES; + NSString *pushTitle = [NSString stringWithFormat:NSLocalizedString(@"Push “%@” to “%@”", @"Contextual Menu Item to push a ref to a specific remote"), refName, remoteName]; + [items addObject:[NSMenuItem pb_itemWithTitle:pushTitle action:@selector(pushDefaultRemoteForRef:) enabled:YES]]; + } + + // push to remotes submenu + NSArray *remoteNames = [repo remotes]; + if ([remoteNames count] && !(hasDefaultRemote && ([remoteNames count] == 1))) { + NSString *pushToTitle = [NSString stringWithFormat:NSLocalizedString(@"Push “%@” to", @"Contextual Menu Submenu Item containing the remotes the selected ref can be pushed to"), refName]; + NSMenuItem *pushToItem = [NSMenuItem pb_itemWithTitle:pushToTitle action:nil enabled:YES]; + NSMenu *remotesMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Remotes Menu", @"Menu listing the repository’s remotes")]; + for (NSString *remote in remoteNames) { + NSMenuItem *remoteItem = [NSMenuItem pb_itemWithTitle:remote action:@selector(pushToRemote:) enabled:YES]; + remoteItem.representedObject = remote; + [remotesMenu addItem:remoteItem]; + } + [pushToItem setSubmenu:remotesMenu]; + pushToItem.representedObject = ref; + [items addObject:pushToItem]; + } + } + + // delete ref + [items addObject:[NSMenuItem separatorItem]]; + BOOL isStash = [[ref ref] hasPrefix:@"refs/stash"]; + BOOL isDeleteEnabled = !(isDetachedHead || isHead || isStash); + if (isDeleteEnabled) { + NSString *deleteFormat = ref.isRemote + ? NSLocalizedString(@"Delete “%@”…", @"Contextual Menu Item to delete a local ref (e.g. branch)") + : NSLocalizedString(@"Remove “%@”…", @"Contextual Menu Item to remove a remote"); + NSString *deleteItemTitle = [NSString stringWithFormat:deleteFormat, refName]; + NSMenuItem *deleteItem = [NSMenuItem pb_itemWithTitle:deleteItemTitle action:@selector(deleteRef:) enabled:YES]; + [items addObject:deleteItem]; + } + + for (NSMenuItem *item in items) { + if (!item.representedObject) { + item.representedObject = ref; + } + } + + return items; +} + + ++ (NSArray *)pb_defaultMenuItemsForCommits:(NSArray *)commits +{ + NSMutableArray *items = [NSMutableArray array]; + + BOOL isSingleCommitSelection = commits.count == 1; + PBGitCommit *firstCommit = commits.firstObject; + + NSString *headBranchName = firstCommit.repository.headRef.ref.shortName; + BOOL isOnHeadBranch = firstCommit.isOnHeadBranch; + BOOL isHead = [firstCommit.OID isEqual:firstCommit.repository.headOID]; + + if (isSingleCommitSelection) { + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Checkout Commit", @"Contextual Menu Item to check out the selected commit") action:@selector(checkout:) enabled:YES]]; + [items addObject:[NSMenuItem separatorItem]]; + + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Create Branch…", @"Contextual Menu Item to create a branch at the selected commit") action:@selector(createBranch:) enabled:YES]]; + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Create Tag…", @"Contextual Menu Item to create a tag at the selected commit") action:@selector(createTag:) enabled:YES]]; + [items addObject:[NSMenuItem separatorItem]]; + } + + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Copy SHA", @"Contextual Menu Item to copy the selected commits’ full SHA(s)") action:@selector(copySHA:) enabled:YES]]; + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Copy short SHA", @"Contextual Menu Item to copy the selected commits’ short SHA(s)") action:@selector(copyShortSHA:) enabled:YES]]; + [items addObject:[NSMenuItem pb_itemWithTitle:NSLocalizedString(@"Copy Patch", @"Contextual Menu Item to copy the selected commits as patch(es)") action:@selector(copyPatch:) enabled:YES]]; + + if (isSingleCommitSelection) { + NSString *diffTitle = [NSString stringWithFormat:NSLocalizedString(@"Diff with “%@”", @"Contextual Menu Item to view a diff between the selected commit and HEAD"), headBranchName]; + [items addObject:[NSMenuItem pb_itemWithTitle:diffTitle action:@selector(diffWithHEAD:) enabled:!isHead]]; + [items addObject:[NSMenuItem separatorItem]]; + + // merge commit + NSString *mergeTitle = isOnHeadBranch + ? NSLocalizedString(@"Merge Commit", @"Inactive Contextual Menu Item for merging commits") + : [NSString stringWithFormat:NSLocalizedString(@"Merge Commit into “%@”", @"Contextual Menu Item to merge the selected commit into HEAD"), headBranchName]; + [items addObject:[NSMenuItem pb_itemWithTitle:mergeTitle action:@selector(merge:) enabled:!isOnHeadBranch]]; + + // cherry pick + NSString *cherryPickTitle = isOnHeadBranch + ? NSLocalizedString(@"Cherry Pick Commit", @"Inactive Contextual Menu Item for cherry-picking commits") + : [NSString stringWithFormat:NSLocalizedString(@"Cherry Pick Commit to “%@”", @"Contextual Menu Item to cherry-pick the selected commit on top of HEAD"), headBranchName]; + [items addObject:[NSMenuItem pb_itemWithTitle:cherryPickTitle action:@selector(cherryPick:) enabled:!isOnHeadBranch]]; + + // rebase + NSString *rebaseTitle = isOnHeadBranch + ? NSLocalizedString(@"Rebase Commit", @"Inactive Contextual Menu Item for rebasing onto commits") + : [NSString stringWithFormat:NSLocalizedString(@"Rebase “%@” onto Commit", @"Contextual Menu Item to rebase the HEAD branch onto the selected commit"), headBranchName]; + [items addObject:[NSMenuItem pb_itemWithTitle:rebaseTitle action:@selector(rebaseHeadBranch:) enabled:!isOnHeadBranch]]; + } + + for (NSMenuItem *item in items) { + if (!item.representedObject) { + item.representedObject = isSingleCommitSelection ? firstCommit : commits; + } + } + + return items; +} + + +@end diff --git a/Classes/Views/PBRemoteProgressSheet.h b/Classes/Views/PBRemoteProgressSheet.h new file mode 100644 index 000000000..76edc362c --- /dev/null +++ b/Classes/Views/PBRemoteProgressSheet.h @@ -0,0 +1,28 @@ +// +// PBRemoteProgressSheetController.h +// GitX +// +// Created by Nathan Kinsinger on 12/6/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import + +#import "RJModalRepoSheet.h" + +NS_ASSUME_NONNULL_BEGIN + +@class PBGitWindowController; + +typedef NSError * _Nullable (^PBProgressSheetExecutionHandler)(void); + +@interface PBRemoteProgressSheet : RJModalRepoSheet + ++ (instancetype)progressSheetWithTitle:(NSString *)title description:(NSString *)description windowController:(PBGitWindowController *)windowController; ++ (instancetype)progressSheetWithTitle:(NSString *)title description:(NSString *)description; + +- (void)beginProgressSheetForBlock:(PBProgressSheetExecutionHandler)executionBlock completionHandler:(void (^)(NSError *))completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/Views/PBRemoteProgressSheet.m b/Classes/Views/PBRemoteProgressSheet.m new file mode 100644 index 000000000..cbc4710dd --- /dev/null +++ b/Classes/Views/PBRemoteProgressSheet.m @@ -0,0 +1,86 @@ +// +// PBRemoteProgressSheetController.m +// GitX +// +// Created by Nathan Kinsinger on 12/6/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import "PBRemoteProgressSheet.h" + +@interface PBRemoteProgressSheet () + +@property (weak) IBOutlet NSTextField *descriptionField; +@property (weak) IBOutlet NSProgressIndicator *progressIndicator; + +@property NSString *title; +@property NSString *progressDescription; + +@end + +@implementation PBRemoteProgressSheet + ++ (instancetype)progressSheetWithTitle:(NSString *)title description:(NSString *)description windowController:(PBGitWindowController *)windowController { + return [[self alloc] initWithTitle:title description:description windowController:windowController]; +} + ++ (instancetype)progressSheetWithTitle:(NSString *)title description:(NSString *)description { + return [[self alloc] initWithTitle:title description:description windowController:nil]; +} + +- (instancetype)initWithTitle:(NSString *)title description:(NSString *)description windowController:(PBGitWindowController *)windowController +{ + if (windowController) { + self = [self initWithWindowNibName:@"PBRemoteProgressSheet" windowController:windowController]; + } else { + self = [self initWithWindowNibName:@"PBRemoteProgressSheet"]; + } + if (!self) return nil; + + _title = title; + _progressDescription = description; + + return self; +} + +- (void)beginProgressSheetForBlock:(PBProgressSheetExecutionHandler)executionBlock completionHandler:(void (^)(NSError *))completionHandler +{ + __block NSError *executionError = nil; + + // Dispatch the actual execution block + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + executionError = executionBlock(); + dispatch_async(dispatch_get_main_queue(), ^{ + [self acceptSheet:self]; + }); + }); + + // Bring up our progress sheet + [self beginSheetWithCompletionHandler:^(id sheet, NSModalResponse returnCode) { + completionHandler(executionError); + }]; +} + +- (void)awakeFromNib { + [self window]; // loads the window (if it wasn't already) + + // resize window if the description is larger than the default text field + NSRect originalFrame = self.descriptionField.frame; + self.descriptionField.stringValue = self.progressDescription; + + NSAttributedString *attributedTitle = self.descriptionField.attributedStringValue; + NSSize boundingSize = originalFrame.size; + boundingSize.height = 0.0f; + NSRect boundingRect = [attributedTitle boundingRectWithSize:boundingSize + options:NSStringDrawingUsesLineFragmentOrigin]; + CGFloat heightDelta = boundingRect.size.height - originalFrame.size.height; + if (heightDelta > 0.0f) { + NSRect windowFrame = [[self window] frame]; + windowFrame.size.height += heightDelta; + [[self window] setFrame:windowFrame display:NO]; + } + + [self.progressIndicator startAnimation:nil]; +} + +@end diff --git a/Classes/Views/PBSourceViewAction.h b/Classes/Views/PBSourceViewAction.h new file mode 100644 index 000000000..763d254df --- /dev/null +++ b/Classes/Views/PBSourceViewAction.h @@ -0,0 +1,17 @@ +// +// PBSourceViewAction.h +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBSourceViewAction : PBSourceViewItem { + NSImage *icon; +} + +@property(retain) NSImage *icon; +@end diff --git a/Classes/Views/PBSourceViewAction.m b/Classes/Views/PBSourceViewAction.m new file mode 100644 index 000000000..344b967b6 --- /dev/null +++ b/Classes/Views/PBSourceViewAction.m @@ -0,0 +1,15 @@ +// +// PBSourceViewAction.m +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "PBSourceViewAction.h" + + +@implementation PBSourceViewAction +@synthesize icon; + +@end diff --git a/Classes/Views/PBSourceViewBadge.h b/Classes/Views/PBSourceViewBadge.h new file mode 100644 index 000000000..1754000d7 --- /dev/null +++ b/Classes/Views/PBSourceViewBadge.h @@ -0,0 +1,19 @@ +// +// PBSourceViewBadge.h +// GitX +// +// Created by Nathan Kinsinger on 2/13/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@interface PBSourceViewBadge : NSObject { + +} + ++ (NSImage *) checkedOutBadgeForCell:(NSTextFieldCell *)cell; ++ (NSImage *) numericBadge:(NSInteger)number forCell:(NSTextFieldCell *)cell; + +@end diff --git a/Classes/Views/PBSourceViewBadge.m b/Classes/Views/PBSourceViewBadge.m new file mode 100644 index 000000000..6d1c35b92 --- /dev/null +++ b/Classes/Views/PBSourceViewBadge.m @@ -0,0 +1,123 @@ +// +// PBSourceViewBadge.m +// GitX +// +// Created by Nathan Kinsinger on 2/13/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBSourceViewBadge.h" +#import "PBSourceViewCell.h" + + +@implementation PBSourceViewBadge + + ++ (NSColor *) badgeHighlightColor +{ + return [NSColor colorWithCalibratedHue:0.612 saturation:0.275 brightness:0.735 alpha:1.000]; +} + + ++ (NSColor *) badgeBackgroundColor +{ + return [NSColor colorWithCalibratedWhite:0.6 alpha:1.00]; +} + + ++ (NSColor *) badgeColorForCell:(NSTextFieldCell *)cell +{ + if ([cell isHighlighted]) + return [NSColor whiteColor]; + + if ([[[cell controlView] window] isMainWindow]) + return [self badgeHighlightColor]; + + return [self badgeBackgroundColor]; +} + + ++ (NSColor *) badgeTextColorForCell:(NSTextFieldCell *)cell +{ + if (![cell isHighlighted]) + return [NSColor whiteColor]; + + if (![[[cell controlView] window] isKeyWindow]) { + if ([[[cell controlView] window] isMainWindow]) { + return [self badgeHighlightColor]; + } else { + return [self badgeBackgroundColor]; + } + } + + if ([[[cell controlView] window] firstResponder] == [cell controlView]) + return [self badgeHighlightColor]; + + return [self badgeBackgroundColor]; +} + + ++ (NSMutableDictionary *) badgeTextAttributes +{ + NSMutableDictionary *badgeTextAttributes = nil; + if (!badgeTextAttributes) { + NSMutableParagraphStyle *centerStyle = [[NSMutableParagraphStyle alloc] init]; + [centerStyle setAlignment:NSCenterTextAlignment]; + + badgeTextAttributes = [NSMutableDictionary dictionary]; + [badgeTextAttributes setObject:[NSFont boldSystemFontOfSize:[NSFont systemFontSize] - 2] forKey:NSFontAttributeName]; + [badgeTextAttributes setObject:centerStyle forKey:NSParagraphStyleAttributeName]; + } + + return badgeTextAttributes; +} + + + +#pragma mark - +#pragma mark badges + ++ (NSImage *) badge:(NSString *)badge forCell:(NSTextFieldCell *)cell +{ + NSColor *badgeColor = [self badgeColorForCell:cell]; + + NSColor *textColor = [self badgeTextColorForCell:cell]; + NSMutableDictionary *badgeTextAttributes = [self badgeTextAttributes]; + [badgeTextAttributes setObject:textColor forKey:NSForegroundColorAttributeName]; + NSAttributedString *badgeString = [[NSAttributedString alloc] initWithString:badge attributes:badgeTextAttributes]; + + CGFloat imageHeight = ceil([badgeString size].height); + CGFloat radius = ceil(imageHeight / 4) * 2; + CGFloat minWidth = ceil(radius * 2.5); + + CGFloat imageWidth = ceil([badgeString size].width + radius); + if (imageWidth < minWidth) + imageWidth = minWidth; + NSRect badgeRect = NSMakeRect(0, 0, imageWidth, imageHeight); + + NSBezierPath *badgePath = [NSBezierPath bezierPathWithRoundedRect:badgeRect xRadius:radius yRadius:radius]; + + NSImage *badgeImage = [[NSImage alloc] initWithSize:badgeRect.size]; + [badgeImage lockFocus]; + + [badgeColor set]; + [badgePath fill]; + + [badgeString drawInRect:badgeRect]; + + [badgeImage unlockFocus]; + + return badgeImage; +} + ++ (NSImage *) checkedOutBadgeForCell:(NSTextFieldCell *)cell +{ + return [self badge:@"✔" forCell:cell]; +} + ++ (NSImage *) numericBadge:(NSInteger)number forCell:(NSTextFieldCell *)cell +{ + return [self badge:[NSString stringWithFormat:@"%ld", number] forCell:cell]; +} + +@end diff --git a/Classes/Views/PBSourceViewCell.h b/Classes/Views/PBSourceViewCell.h new file mode 100644 index 000000000..5b8839245 --- /dev/null +++ b/Classes/Views/PBSourceViewCell.h @@ -0,0 +1,19 @@ +// +// PBSourceViewCell.h +// GitX +// +// Created by Nathan Kinsinger on 1/7/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBIconAndTextCell.h" + + +@interface PBSourceViewCell : PBIconAndTextCell { + BOOL isCheckedOut; +} + +@property (assign) BOOL isCheckedOut; + +@end diff --git a/Classes/Views/PBSourceViewCell.m b/Classes/Views/PBSourceViewCell.m new file mode 100644 index 000000000..3644599fd --- /dev/null +++ b/Classes/Views/PBSourceViewCell.m @@ -0,0 +1,58 @@ +// +// PBSourceViewCell.m +// GitX +// +// Created by Nathan Kinsinger on 1/7/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBSourceViewCell.h" +#import "PBGitSidebarController.h" +#import "PBSourceViewBadge.h" + + + + +@implementation PBSourceViewCell + +@synthesize isCheckedOut; + +# pragma mark context menu delegate methods + +- (NSMenu *) menuForEvent:(NSEvent *)event inRect:(NSRect)rect ofView:(NSOutlineView *)view +{ + NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil]; + NSInteger row = [view rowAtPoint:point]; + + PBGitSidebarController *controller = (PBGitSidebarController*)[view delegate]; + + return [controller menuForRow:row]; +} + + +#pragma mark drawing + +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)outlineView +{ + if (isCheckedOut) { + NSImage *checkedOutImage = [PBSourceViewBadge checkedOutBadgeForCell:self]; + NSSize imageSize = [checkedOutImage size]; + NSRect imageFrame; + NSDivideRect(cellFrame, &imageFrame, &cellFrame, imageSize.width + 3, NSMaxXEdge); + imageFrame.size = imageSize; + + // center the decal verctically + imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2); + + [checkedOutImage drawInRect:imageFrame + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:1.0f + respectFlipped:YES + hints:nil]; + } + + [super drawWithFrame:cellFrame inView:outlineView]; +} + +@end diff --git a/Classes/Views/PBSourceViewItem.h b/Classes/Views/PBSourceViewItem.h new file mode 100644 index 000000000..945318f18 --- /dev/null +++ b/Classes/Views/PBSourceViewItem.h @@ -0,0 +1,48 @@ +// +// PBSourceViewItem.h +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import + +@class PBGitRevSpecifier; +@class PBGitRef; + +@interface PBSourceViewItem : NSObject { + NSMutableOrderedSet *childrenSet; + + NSString *title; + PBGitRevSpecifier *revSpecifier; + + BOOL isGroupItem; + BOOL isUncollapsible; +} + ++ (id)groupItemWithTitle:(NSString *)title; ++ (id)itemWithRevSpec:(PBGitRevSpecifier *)revSpecifier; ++ (id)itemWithTitle:(NSString *)title; + +- (void)addChild:(PBSourceViewItem *)child; +- (void)removeChild:(PBSourceViewItem *)child; +- (NSImage*)iconNamed:(NSString*)name; + +// This adds the ref to the path, which should match the item's title, +// so "refs/heads/pu/pb/sidebar" would have the path [@"pu", @"pb", @"sidebare"] +// to the 'local' branch thing +- (void)addRev:(PBGitRevSpecifier *)revSpecifier toPath:(NSArray *)path; +- (PBSourceViewItem *)findRev:(PBGitRevSpecifier *)rev; + +- (PBGitRef *) ref; + +@property NSString *title; +@property(nonatomic, readonly) NSArray *sortedChildren; +@property(assign) BOOL isGroupItem, isUncollapsible, isExpanded; +@property PBGitRevSpecifier *revSpecifier; +@property (assign)PBSourceViewItem *parent; +@property(readonly) NSString *iconName; +@property(readonly) NSImage *icon; + +@end diff --git a/Classes/Views/PBSourceViewItem.m b/Classes/Views/PBSourceViewItem.m new file mode 100644 index 000000000..e4ae40602 --- /dev/null +++ b/Classes/Views/PBSourceViewItem.m @@ -0,0 +1,172 @@ +// +// PBSourceViewItem.m +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "PBSourceViewItem.h" +#import "PBSourceViewItems.h" +#import "PBGitRef.h" + +@interface PBSourceViewItem () + +@property (nonatomic, strong) NSArray *sortedChildren; + +@end + +@implementation PBSourceViewItem + +@synthesize parent, isGroupItem, revSpecifier, isUncollapsible, isExpanded; +@dynamic icon; + +- (id)init +{ + if (!(self = [super init])) + return nil; + + childrenSet = [[NSMutableOrderedSet alloc] init]; + return self; +} + ++ (id)itemWithTitle:(NSString *)title +{ + PBSourceViewItem *item = [[[self class] alloc] init]; + item.title = title; + return item; +} + ++ (id)groupItemWithTitle:(NSString *)title +{ + PBSourceViewItem *item = [self itemWithTitle:[title uppercaseString]]; + item.isGroupItem = YES; + return item; +} + ++ (id)itemWithRevSpec:(PBGitRevSpecifier *)revSpecifier +{ + PBGitRef *ref = [revSpecifier ref]; + + if ([ref isTag]) + return [PBGitSVTagItem tagItemWithRevSpec:revSpecifier]; + else if ([ref isBranch]) + return [PBGitSVBranchItem branchItemWithRevSpec:revSpecifier]; + else if ([ref isRemoteBranch]) + return [PBGitSVRemoteBranchItem remoteBranchItemWithRevSpec:revSpecifier]; + + return [PBGitSVOtherRevItem otherItemWithRevSpec:revSpecifier]; +} + +- (NSArray *)sortedChildren +{ + if (!self->_sortedChildren) { + NSArray *newArray = [childrenSet sortedArrayUsingComparator:^NSComparisonResult(PBSourceViewItem *obj1, PBSourceViewItem *obj2) { + return [obj1.title localizedStandardCompare:obj2.title]; + }]; + self.sortedChildren = newArray; + } + return [NSArray arrayWithArray:self->_sortedChildren]; +} + +- (void)addChild:(PBSourceViewItem *)child +{ + if (!child) + return; + + [childrenSet addObject:child]; + self.sortedChildren = nil; + child.parent = self; +} + +- (void)removeChild:(PBSourceViewItem *)child +{ + if (!child) + return; + + [childrenSet removeObject:child]; + self.sortedChildren = nil; + if (!self.isGroupItem && ([childrenSet count] == 0)) + [self.parent removeChild:self]; +} + +- (void)addRev:(PBGitRevSpecifier *)theRevSpecifier toPath:(NSArray *)path +{ + if ([path count] == 1) { + PBSourceViewItem *item = [PBSourceViewItem itemWithRevSpec:theRevSpecifier]; + [self addChild:item]; + return; + } + + NSString *firstTitle = [path objectAtIndex:0]; + PBSourceViewItem *node = nil; + for (PBSourceViewItem *child in childrenSet) + if ([child.title isEqualToString:firstTitle]) + node = child; + + if (!node) { + if ([firstTitle isEqualToString:[[theRevSpecifier ref] remoteName]]) + node = [PBGitSVRemoteItem remoteItemWithTitle:firstTitle]; + else { + node = [PBGitSVFolderItem folderItemWithTitle:firstTitle]; + node.isExpanded = [[self title] isEqualToString:@"BRANCHES"]; + } + [self addChild:node]; + } + + [node addRev:theRevSpecifier toPath:[path subarrayWithRange:NSMakeRange(1, [path count] - 1)]]; +} + +- (PBSourceViewItem *)findRev:(PBGitRevSpecifier *)rev +{ + if ([rev isEqual:revSpecifier]) + return self; + + PBSourceViewItem *item = nil; + for (PBSourceViewItem *child in childrenSet) + if ( (item = [child findRev:rev]) != nil ) + return item; + + return nil; +} + +- (NSImage *) icon +{ + return [self iconNamed:[self iconName]]; +} + +- (NSImage *)iconNamed:(NSString*)name +{ + NSImage* iconImage = [NSImage imageNamed:name]; + [iconImage setSize:NSMakeSize(16,16)]; + [iconImage setCacheMode:NSImageCacheAlways]; + return iconImage; +} + +- (NSString *)title +{ + if (title) + return title; + + return [[revSpecifier description] lastPathComponent]; +} + +- (void) setTitle:(NSString *)_title +{ + title = _title; +} + +- (NSString *) stringValue +{ + return self.title; +} + +- (PBGitRef *) ref +{ + if (self.revSpecifier) + return [self.revSpecifier ref]; + + return nil; +} + +@end diff --git a/Classes/Views/PBSourceViewItems.h b/Classes/Views/PBSourceViewItems.h new file mode 100644 index 000000000..fef5ba8d8 --- /dev/null +++ b/Classes/Views/PBSourceViewItems.h @@ -0,0 +1,20 @@ +// +// PBSourceViewItems.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBSourceViewItem.h" + +#import "PBGitSVStageItem.h" + +#import "PBGitRevSpecifier.h" +#import "PBGitSVBranchItem.h" +#import "PBGitSVRemoteItem.h" +#import "PBGitSVRemoteBranchItem.h" +#import "PBGitSVTagItem.h" +#import "PBGitSVOtherRevItem.h" +#import "PBGitSVFolderItem.h" +#import "PBGitSVSubmoduleItem.h" \ No newline at end of file diff --git a/Classes/Views/PBSourceViewRemote.h b/Classes/Views/PBSourceViewRemote.h new file mode 100644 index 000000000..edb5d988b --- /dev/null +++ b/Classes/Views/PBSourceViewRemote.h @@ -0,0 +1,16 @@ +// +// PBSourceViewRemote.h +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBSourceViewRemote : PBSourceViewItem { + +} + +@end diff --git a/Classes/Views/PBSourceViewRemote.m b/Classes/Views/PBSourceViewRemote.m new file mode 100644 index 000000000..b8ccd8482 --- /dev/null +++ b/Classes/Views/PBSourceViewRemote.m @@ -0,0 +1,18 @@ +// +// PBSourceViewRemote.m +// GitX +// +// Created by Pieter de Bie on 9/8/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import "PBSourceViewRemote.h" + + +@implementation PBSourceViewRemote + +- (NSImage *)icon +{ + return [NSImage imageNamed:@"remote"]; +} +@end diff --git a/Classes/git/GTOID+JavaScript.h b/Classes/git/GTOID+JavaScript.h new file mode 100644 index 000000000..788e76882 --- /dev/null +++ b/Classes/git/GTOID+JavaScript.h @@ -0,0 +1,13 @@ +// +// GTOID+JavaScript.h +// GitX +// +// Created by Sven Weidauer on 18.05.14. +// +// + +#import + +@interface GTOID (JavaScript) + +@end diff --git a/Classes/git/GTOID+JavaScript.m b/Classes/git/GTOID+JavaScript.m new file mode 100644 index 000000000..85498934f --- /dev/null +++ b/Classes/git/GTOID+JavaScript.m @@ -0,0 +1,22 @@ +// +// GTOID+JavaScript.m +// GitX +// +// Created by Sven Weidauer on 18.05.14. +// +// + +#import "GTOID+JavaScript.h" + +@implementation GTOID (JavaScript) + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return NO; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name { + return NO; +} + +@end diff --git a/Classes/git/GitRepoFinder.m b/Classes/git/GitRepoFinder.m new file mode 100644 index 000000000..7f88e9301 --- /dev/null +++ b/Classes/git/GitRepoFinder.m @@ -0,0 +1,68 @@ +// +// GitRepoFinder.m +// GitX +// +// Created by Rowan James on 13/11/2012. +// +// + +#import "GitRepoFinder.h" + +@implementation GitRepoFinder + ++ (NSURL*)workDirForURL:(NSURL*)fileURL; +{ + if (!fileURL.isFileURL) + { + return nil; + } + git_repository* repo = nil; + git_repository_open_ext(&repo, fileURL.path.UTF8String, GIT_REPOSITORY_OPEN_CROSS_FS, NULL); + if (!repo) + { + return nil; + } + 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; +} + +@end diff --git a/PBGitBinary.h b/Classes/git/PBGitBinary.h similarity index 88% rename from PBGitBinary.h rename to Classes/git/PBGitBinary.h index 5008b3de8..1d9fb8fe6 100644 --- a/PBGitBinary.h +++ b/Classes/git/PBGitBinary.h @@ -10,9 +10,7 @@ #define MIN_GIT_VERSION "1.6.0" -@interface PBGitBinary : NSObject { - -} +@interface PBGitBinary : NSObject + (NSString *) path; + (NSString *) version; diff --git a/Classes/git/PBGitBinary.m b/Classes/git/PBGitBinary.m new file mode 100644 index 000000000..6040bc3a9 --- /dev/null +++ b/Classes/git/PBGitBinary.m @@ -0,0 +1,155 @@ +// +// PBGitBinary.m +// GitX +// +// Created by Pieter de Bie on 04-10-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitBinary.h" +#import "PBTask.h" + +@implementation PBGitBinary + +static NSString* gitPath = nil; + ++ (NSString *)versionForPath:(NSString *)path +{ + if (!path) + return nil; + + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) + return nil; + + NSString *version = [PBTask outputForCommand:path arguments:@[@"--version"] error:NULL]; + + return [self extractGitVersion:version]; +} + ++ (NSString *) extractGitVersion:(NSString *)versionString +{ + NSError * error; + NSRegularExpression * regex = [NSRegularExpression regularExpressionWithPattern:@"git version ([0-9.]+)" + options:0 + error:&error]; + NSTextCheckingResult * result = [regex firstMatchInString:versionString + options:0 + range:NSMakeRange(0, versionString.length)]; + if (result != nil && result.numberOfRanges == 2) { + return [versionString substringWithRange:[result rangeAtIndex:1]]; + } + return nil; +} + ++ (BOOL) acceptBinary:(NSString *)path +{ + if (!path) + return NO; + + NSString *version = [self versionForPath:path]; + if (!version) + return NO; + + int c = [version compare:@"" MIN_GIT_VERSION options:NSNumericSearch]; + if (c == NSOrderedSame || c == NSOrderedDescending) { + gitPath = path; + return YES; + } + + NSLog(@"Found a git binary at %@, but is only version %@", path, version); + return NO; +} + ++ (void) initialize +{ + // Check what we might have in user defaults + // NOTE: Currently this should NOT have a registered default, or the searching bits below won't work + gitPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"gitExecutable"]; + if (gitPath.length > 0) { + if ([self acceptBinary:gitPath]) + return; + [[NSAlert alertWithMessageText:NSLocalizedString(@"Invalid git path", @"Error message for NSUserDefaults configured path to git binary that does not point to a git binary") + defaultButton:NSLocalizedString(@"OK", @"OK") + alternateButton:nil + otherButton:nil + informativeTextWithFormat:NSLocalizedString( + @"The path „%@“, which is configured as a custom git path in the " + "preferences window, is not a valid git v" MIN_GIT_VERSION " or higher binary. " + "Using the default search paths instead.", + "Informative text for NSUserDefaults configured path to git binary that does not point to a git binary"), + gitPath] + runModal]; + } + + // Try to find the path of the Git binary + char* path = getenv("GIT_PATH"); + if (path && [self acceptBinary:[NSString stringWithUTF8String:path]]) + return; + + // No explicit path. + + // Try to find git with "which" + NSString* whichPath = [PBTask outputForCommand:@"/usr/bin/which" arguments:@[@"git"] error:NULL]; + if ([self acceptBinary:whichPath]) + return; + + // Still no path. Let's try some default locations. + for (NSString* location in [PBGitBinary searchLocations]) { + if ([self acceptBinary:location]) + return; + } + + // Lastly, try `xcrun git` + NSString* xcrunPath = [PBTask outputForCommand:@"/usr/bin/xcrun" arguments:@[@"-f", @"git"] error:NULL]; + if ([self acceptBinary:xcrunPath]) + { + return; + } + + NSLog(@"Could not find a git binary higher than version " MIN_GIT_VERSION); +} + ++ (NSString *) path; +{ + return gitPath; +} + +static NSMutableArray *locations = nil; + ++ (NSArray *) searchLocations +{ + if (!locations) + { + locations = [[NSMutableArray alloc] initWithObjects: + @"/opt/local/bin/git", + @"/sw/bin/git", + @"/opt/git/bin/git", + @"/usr/local/bin/git", + @"/usr/local/git/bin/git", + nil]; + + [locations addObject:[@"~/bin/git" stringByExpandingTildeInPath]]; + [locations addObject:@"/usr/bin/git"]; + } + return locations; +} + ++ (NSString *) notFoundError +{ + NSString * searchPathsString = [[PBGitBinary searchLocations] componentsJoinedByString:@"\n\t"]; + return [NSString stringWithFormat: + NSLocalizedString( + @"Could not find a git binary version " MIN_GIT_VERSION " or higher.\n" + @"Please make sure there is a git binary in one of the following locations:" + @"\n\n\t%s", + @"Error message when no git client can be found."), + searchPathsString]; +} + + ++ (NSString *)version +{ + return [self versionForPath:gitPath]; +} + +@end diff --git a/Classes/git/PBGitCommit.h b/Classes/git/PBGitCommit.h new file mode 100644 index 000000000..a1554fc64 --- /dev/null +++ b/Classes/git/PBGitCommit.h @@ -0,0 +1,65 @@ +// +// PBGitCommit.h +// GitTest +// +// Created by Pieter de Bie on 13-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBGitRefish.h" // for @protocol PBGitRefish + +@class PBGitRepository; +@class PBGitTree; +@class PBGitRef; +@class PBGraphCellInfo; + +extern NSString * const kGitXCommitType; + + +@interface PBGitCommit : NSObject + +@property (nonatomic, weak, readonly) PBGitRepository* repository; + +@property (nonatomic, strong, readonly) GTCommit *gtCommit; +@property (nonatomic, strong, readonly) GTOID *OID; + +@property (nonatomic, strong, readonly) NSDate *date; +@property (nonatomic, strong, readonly) NSString *subject; +@property (nonatomic, strong, readonly) NSString *message; +@property (nonatomic, strong, readonly) NSString *author; +@property (nonatomic, strong, readonly) NSString *authorEmail; +@property (nonatomic, strong, readonly) NSString *authorDate; +@property (nonatomic, strong, readonly) NSString *committer; +@property (nonatomic, strong, readonly) NSString *committerEmail; +@property (nonatomic, strong, readonly) NSString *committerDate; +@property (nonatomic, strong, readonly) NSString *details; +@property (nonatomic, strong, readonly) NSString *patch; +@property (nonatomic, strong, readonly) NSString *SHA; +@property (nonatomic, strong, readonly) NSString *SVNRevision; + +@property (nonatomic, copy, readonly) NSArray *parents; +@property NSMutableArray* refs; + +@property (nonatomic, strong) PBGraphCellInfo *lineInfo; + +@property (nonatomic, readonly) PBGitTree* tree; +@property (readonly) NSArray* treeContents; + + +- (id)initWithRepository:(PBGitRepository *)repo andCommit:(GTCommit *)gtCommit; + +- (void) addRef:(PBGitRef *)ref; +- (void) removeRef:(id)ref; +- (BOOL) hasRef:(PBGitRef *)ref; + +- (NSString *)SHA; +- (BOOL) isOnSameBranchAs:(PBGitCommit *)other; +- (BOOL) isOnHeadBranch; + +// +- (NSString *) refishName; +- (NSString *) shortName; +- (NSString *) refishType; + +@end diff --git a/Classes/git/PBGitCommit.m b/Classes/git/PBGitCommit.m new file mode 100644 index 000000000..b720cc87a --- /dev/null +++ b/Classes/git/PBGitCommit.m @@ -0,0 +1,281 @@ +// +// PBGitCommit.m +// GitTest +// +// Created by Pieter de Bie on 13-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitRepository.h" +#import "PBGitRepository_PBGitBinarySupport.h" +#import "PBGitCommit.h" +#import "PBGitTree.h" +#import "PBGitRef.h" +#import "PBGitDefaults.h" +#import "ObjectiveGit+PBCategories.h" + +NSString * const kGitXCommitType = @"commit"; + +@interface PBGitCommit () + +@property (nonatomic, weak) PBGitRepository *repository; +@property (nonatomic, strong) GTCommit *gtCommit; +@property (nonatomic, copy) NSArray *parents; + +@property (nonatomic, strong) NSString *patch; +@property (nonatomic, strong) GTOID *oid; + +@end + + +@implementation PBGitCommit + ++ (NSDateFormatter *)longDateFormatter { + static NSDateFormatter *formatter = nil; + static dispatch_once_t token; + dispatch_once(&token, ^{ + NSDateFormatter *f = [[NSDateFormatter alloc] init]; + f.dateStyle = NSDateFormatterLongStyle; + f.timeStyle = NSDateFormatterLongStyle; + formatter = f; + }); + return formatter; +} + +- (NSDate *) date +{ + return self.gtCommit.commitDate; + // previous behaviour was equiv. to: return self.gtCommit.author.time; +} + +- (NSString *) dateString +{ + NSDateFormatter * formatter = [[NSDateFormatter alloc] init]; + [formatter setLocalizedDateFormatFromTemplate:@"%Y-%m-%d %H:%M:%S"]; + return [formatter stringFromDate:self.date]; +} + +- (NSArray*) treeContents +{ + return self.tree.children; +} + +- (id)initWithRepository:(PBGitRepository *)repo andCommit:(GTCommit *)gtCommit +{ + self = [super init]; + if (!self) { + return nil; + } + self.repository = repo; + self.gtCommit = gtCommit; + + return self; +} + + +- (NSArray *)parents +{ + if (!self->_parents) { + self.parents = self.gtCommit.parentOIDs; + } + return self->_parents; +} + +- (NSString *)subject +{ + return self.gtCommit.messageSummary; +} + +- (NSString *)message +{ + return self.gtCommit.message; +} + +- (NSString *)author +{ + NSString *result = self.gtCommit.author.name; + return result; +} + +- (NSString *)authorEmail +{ + return self.gtCommit.author.email; +} + +- (NSString *)authorDate +{ + return [[[self class] longDateFormatter] stringFromDate:self.gtCommit.author.time]; +} + +- (NSString *)committer +{ + GTSignature *sig = self.gtCommit.committer; + return sig.name; +} + +- (NSString *)committerEmail +{ + return self.gtCommit.committer.email; +} + +- (NSString *)committerDate +{ + return [[[self class] longDateFormatter] stringFromDate:self.gtCommit.committer.time]; +} + +- (NSString *)SVNRevision +{ + NSString *result = nil; + if ([self.repository hasSVNRemote]) + { + // get the git-svn-id from the message + NSArray *matches = nil; + NSString *string = self.gtCommit.message; + NSError *error = nil; + // Regular expression for pulling out the SVN revision from the git log + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^git-svn-id: .*@(\\d+) .*$" options:NSRegularExpressionAnchorsMatchLines error:&error]; + + if (string) { + matches = [regex matchesInString:string options:0 range:NSMakeRange(0, [string length])]; + for (NSTextCheckingResult *match in matches) + { + NSRange matchRange = [match rangeAtIndex:1]; + NSString *matchString = [string substringWithRange:matchRange]; + result = matchString; + } + } + } + return result; +} + +- (GTOID *)OID +{ + if (!_oid) { + _oid = self.gtCommit.OID; + } + return _oid; +} + +- (NSString *)SHA +{ + return self.OID.SHA; +} + +- (BOOL) isOnSameBranchAs:(PBGitCommit *)otherCommit +{ + if (!otherCommit) + return NO; + + if ([self isEqual:otherCommit]) + return YES; + + return [self.repository isOIDOnSameBranch:otherCommit.OID asOID:self.OID]; +} + +- (BOOL) isOnHeadBranch +{ + return [self isOnSameBranchAs:[self.repository headCommit]]; +} + +- (BOOL)isEqual:(id)otherCommit +{ + if (self == otherCommit) { + return YES; + } + return NO; +} + +- (NSUInteger)hash +{ + return self.OID.hash; +} + +- (NSString *) patch +{ + if (self->_patch != nil) + return _patch; + + NSError *error = nil; + NSString *p = [self.repository outputOfTaskWithArguments:@[@"format-patch", @"-1", @"--stdout", self.SHA] error:&error]; + if (!p) { + PBLogError(error); + return nil; + } + + // Add a GitX identifier to the patch ;) + self.patch = [[p substringToIndex:[p length] -1] stringByAppendingString:@"+GitX"]; + return self->_patch; +} + +- (PBGitTree*) tree +{ + return [PBGitTree rootForCommit: self]; +} + +- (void)addRef:(PBGitRef *)ref +{ + if (!self.refs) + self.refs = [NSMutableArray arrayWithObject:ref]; + else + [self.refs addObject:ref]; +} + +- (void)removeRef:(id)ref +{ + if (!self.refs) + return; + + [self.refs removeObject:ref]; +} + +- (BOOL) hasRef:(PBGitRef *)ref +{ + if (!self.refs) + return NO; + + for (PBGitRef *existingRef in self.refs) + if ([existingRef isEqualToRef:ref]) + return YES; + + return NO; +} + +- (NSMutableArray *)refs +{ + return self.repository.refs[self.OID]; +} + +- (void) setRefs:(NSMutableArray *)refs +{ + self.repository.refs[self.OID] = [NSMutableArray arrayWithArray:refs]; +} + + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return NO; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name { + return NO; +} + + +#pragma mark + +- (NSString *) refishName +{ + return self.SHA; +} + +- (NSString *) shortName +{ + return self.gtCommit.shortSHA; +} + +- (NSString *) refishType +{ + return kGitXCommitType; +} + +@end diff --git a/Classes/git/PBGitDefaults.h b/Classes/git/PBGitDefaults.h new file mode 100644 index 000000000..be85cdb5b --- /dev/null +++ b/Classes/git/PBGitDefaults.h @@ -0,0 +1,45 @@ +// +// PBGitDefaults.h +// GitX +// +// Created by Jeff Mesnil on 19/10/08. +// Copyright 2008 Jeff Mesnil (http://jmesnil.net/). All rights reserved. +// + + +#define kDialogAcceptDroppedRef @"Accept Dropped Ref" +#define kDialogConfirmPush @"Confirm Push" +#define kDialogDeleteRef @"Delete Ref" + +@interface PBGitDefaults : NSObject +{ + +} + ++ (NSInteger) commitMessageViewVerticalLineLength; ++ (NSInteger) commitMessageViewVerticalBodyLineLength; ++ (BOOL) commitMessageViewHasVerticalLine; ++ (BOOL) isGistEnabled; ++ (BOOL) isGravatarEnabled; ++ (BOOL) confirmPublicGists; ++ (BOOL) isGistPublic; ++ (BOOL)showWhitespaceDifferences; ++ (BOOL) shouldCheckoutBranch; ++ (void) setShouldCheckoutBranch:(BOOL)shouldCheckout; ++ (NSString *) recentCloneDestination; ++ (void) setRecentCloneDestination:(NSString *)path; ++ (BOOL) showStageView; ++ (void) setShowStageView:(BOOL)suppress; ++ (NSInteger) branchFilter; ++ (void) setBranchFilter:(NSInteger)state; ++ (NSInteger)historySearchMode; ++ (void)setHistorySearchMode:(NSInteger)mode; ++ (BOOL)useRepositoryWatcher; + + +// Suppressed Dialog Warnings ++ (void)suppressDialogWarningForDialog:(NSString *)dialog; ++ (BOOL)isDialogWarningSuppressedForDialog:(NSString *)dialog; ++ (void)resetAllDialogWarnings; + +@end diff --git a/Classes/git/PBGitDefaults.m b/Classes/git/PBGitDefaults.m new file mode 100644 index 000000000..dc3921f3c --- /dev/null +++ b/Classes/git/PBGitDefaults.m @@ -0,0 +1,229 @@ +// +// PBGitDefaults.m +// GitX +// +// Created by Jeff Mesnil on 19/10/08. +// Copyright 2008 Jeff Mesnil (http://jmesnil.net/). All rights reserved. +// + +#import "PBGitDefaults.h" +#import "PBHistorySearchController.h" + +#define kDefaultVerticalLineLength 50 +#define kCommitMessageViewVerticalLineLength @"PBCommitMessageViewVerticalLineLength" +#define kDefaultVerticalBodyLineLength 72 +#define kCommitMessageViewVerticalBodyLineLength @"PBCommitMessageViewVerticalBodyLineLength" +#define kCommitMessageViewHasVerticalLine @"PBCommitMessageViewHasVerticalLine" +#define kEnableGist @"PBEnableGist" +#define kEnableGravatar @"PBEnableGravatar" +#define kConfirmPublicGists @"PBConfirmPublicGists" +#define kPublicGist @"PBGistPublic" +#define kShowWhitespaceDifferences @"PBShowWhitespaceDifferences" +#define kOpenCurDirOnLaunch @"PBOpenCurDirOnLaunch" +#define kShowOpenPanelOnLaunch @"PBShowOpenPanelOnLaunch" +#define kShouldCheckoutBranch @"PBShouldCheckoutBranch" +#define kRecentCloneDestination @"PBRecentCloneDestination" +#define kShowStageView @"PBShowStageView" +#define kOpenPreviousDocumentsOnLaunch @"PBOpenPreviousDocumentsOnLaunch" +#define kPreviousDocumentPaths @"PBPreviousDocumentPaths" +#define kBranchFilterState @"PBBranchFilter" +#define kHistorySearchMode @"PBHistorySearchMode" +#define kSuppressedDialogWarnings @"Suppressed Dialog Warnings" +#define kUseRepositoryWatcher @"PBUseRepositoryWatcher" + +@implementation PBGitDefaults + ++ (void)initialize +{ + NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary]; + [defaultValues setObject:[NSNumber numberWithInt:kDefaultVerticalLineLength] + forKey:kCommitMessageViewVerticalLineLength]; + [defaultValues setObject:[NSNumber numberWithInt:kDefaultVerticalBodyLineLength] + forKey:kCommitMessageViewVerticalBodyLineLength]; + [defaultValues setObject:[NSNumber numberWithBool:YES] + forKey:kCommitMessageViewHasVerticalLine]; + [defaultValues setObject:[NSNumber numberWithBool:NO] + forKey:kEnableGist]; + [defaultValues setObject:[NSNumber numberWithBool:YES] + forKey:kEnableGravatar]; + [defaultValues setObject:[NSNumber numberWithBool:NO] + forKey:kConfirmPublicGists]; + [defaultValues setObject:[NSNumber numberWithBool:NO] + forKey:kPublicGist]; + [defaultValues setObject:[NSNumber numberWithBool:YES] + forKey:kShowWhitespaceDifferences]; + [defaultValues setObject:[NSNumber numberWithBool:YES] + forKey:kOpenCurDirOnLaunch]; + [defaultValues setObject:[NSNumber numberWithBool:YES] + forKey:kShowOpenPanelOnLaunch]; + [defaultValues setObject:[NSNumber numberWithBool:YES] + forKey:kShouldCheckoutBranch]; + [defaultValues setObject:[NSNumber numberWithBool:NO] + forKey:kOpenPreviousDocumentsOnLaunch]; + [defaultValues setObject:[NSNumber numberWithInteger:PBHistorySearchModeBasic] + forKey:kHistorySearchMode]; + [defaultValues setObject:[NSNumber numberWithBool:YES] + forKey:kUseRepositoryWatcher]; + [[NSUserDefaults standardUserDefaults] registerDefaults:defaultValues]; +} + ++ (NSInteger) commitMessageViewVerticalLineLength +{ + return [[NSUserDefaults standardUserDefaults] integerForKey:kCommitMessageViewVerticalLineLength]; +} + ++ (BOOL) commitMessageViewHasVerticalLine +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kCommitMessageViewHasVerticalLine]; +} + ++ (NSInteger) commitMessageViewVerticalBodyLineLength +{ + return [[NSUserDefaults standardUserDefaults] integerForKey:kCommitMessageViewVerticalBodyLineLength]; +} + ++ (BOOL) isGistEnabled +{ + return NO; +} + ++ (BOOL) isGravatarEnabled +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kEnableGravatar]; +} + ++ (BOOL) confirmPublicGists +{ + return NO; +} + ++ (BOOL) isGistPublic +{ + return NO; +} + ++ (BOOL)showWhitespaceDifferences +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kShowWhitespaceDifferences]; +} + ++ (BOOL)openCurDirOnLaunch +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kOpenCurDirOnLaunch]; +} + ++ (BOOL)showOpenPanelOnLaunch +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kShowOpenPanelOnLaunch]; +} + ++ (BOOL) shouldCheckoutBranch +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kShouldCheckoutBranch]; +} + ++ (void) setShouldCheckoutBranch:(BOOL)shouldCheckout +{ + [[NSUserDefaults standardUserDefaults] setBool:shouldCheckout forKey:kShouldCheckoutBranch]; +} + ++ (NSString *) recentCloneDestination +{ + return [[NSUserDefaults standardUserDefaults] stringForKey:kRecentCloneDestination]; +} + ++ (void) setRecentCloneDestination:(NSString *)path +{ + [[NSUserDefaults standardUserDefaults] setObject:path forKey:kRecentCloneDestination]; +} + ++ (BOOL) showStageView +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kShowStageView]; +} + ++ (void) setShowStageView:(BOOL)suppress +{ + return [[NSUserDefaults standardUserDefaults] setBool:suppress forKey:kShowStageView]; +} + ++ (BOOL) openPreviousDocumentsOnLaunch +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kOpenPreviousDocumentsOnLaunch]; +} + ++ (void) setPreviousDocumentPaths:(NSArray *)documentPaths +{ + [[NSUserDefaults standardUserDefaults] setObject:documentPaths forKey:kPreviousDocumentPaths]; +} + ++ (NSArray *) previousDocumentPaths +{ + return [[NSUserDefaults standardUserDefaults] arrayForKey:kPreviousDocumentPaths]; +} + ++ (void) removePreviousDocumentPaths +{ + [[NSUserDefaults standardUserDefaults] removeObjectForKey:kPreviousDocumentPaths]; +} ++ (NSInteger) branchFilter +{ + return [[NSUserDefaults standardUserDefaults] integerForKey:kBranchFilterState]; +} + ++ (void) setBranchFilter:(NSInteger)state +{ + [[NSUserDefaults standardUserDefaults] setInteger:state forKey:kBranchFilterState]; +} + ++ (NSInteger)historySearchMode +{ + return [[NSUserDefaults standardUserDefaults] integerForKey:kHistorySearchMode]; +} + ++ (void)setHistorySearchMode:(NSInteger)mode +{ + [[NSUserDefaults standardUserDefaults] setInteger:mode forKey:kHistorySearchMode]; +} + + + +// Suppressed Dialog Warnings +// +// Represents dialogs where the user has checked the "Do not show this message again" checkbox. +// Keep these together in an array to make it easier to reset all the warnings. + ++ (NSSet *)suppressedDialogWarnings +{ + NSSet *suppressedDialogWarnings = [NSSet setWithArray:[[NSUserDefaults standardUserDefaults] arrayForKey:kSuppressedDialogWarnings]]; + if (suppressedDialogWarnings == nil) + suppressedDialogWarnings = [NSSet set]; + + return suppressedDialogWarnings; +} + ++ (void)suppressDialogWarningForDialog:(NSString *)dialog +{ + NSSet *suppressedDialogWarnings = [[self suppressedDialogWarnings] setByAddingObject:dialog]; + + [[NSUserDefaults standardUserDefaults] setObject:[suppressedDialogWarnings allObjects] forKey:kSuppressedDialogWarnings]; +} + ++ (BOOL)isDialogWarningSuppressedForDialog:(NSString *)dialog +{ + return [[self suppressedDialogWarnings] containsObject:dialog]; +} + ++ (void)resetAllDialogWarnings +{ + [[NSUserDefaults standardUserDefaults] setObject:nil forKey:kSuppressedDialogWarnings]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + + ++ (BOOL) useRepositoryWatcher +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kUseRepositoryWatcher]; +} + +@end diff --git a/PBGitGraphLine.h b/Classes/git/PBGitGraphLine.h similarity index 100% rename from PBGitGraphLine.h rename to Classes/git/PBGitGraphLine.h diff --git a/PBGitGraphLine.m b/Classes/git/PBGitGraphLine.m similarity index 98% rename from PBGitGraphLine.m rename to Classes/git/PBGitGraphLine.m index b4a36e7bc..d9ebcd64a 100644 --- a/PBGitGraphLine.m +++ b/Classes/git/PBGitGraphLine.m @@ -8,6 +8,7 @@ #import "PBGitGraphLine.h" +/* @implementation PBGitGraphLine @synthesize upper, from, to, colorIndex; - (id)initWithUpper: (char) u From: (char) f to: (char) t color: (char) c; @@ -28,4 +29,4 @@ + (PBGitGraphLine*) upperLineFrom:(char) f to: (char) t color: (char) c { return [[PBGitGraphLine alloc] initWithUpper:1 From:f to:t color: c]; } -@end +@end */ diff --git a/PBGitGrapher.h b/Classes/git/PBGitGrapher.h similarity index 55% rename from PBGitGrapher.h rename to Classes/git/PBGitGrapher.h index d17f3bd3f..df6c5de49 100644 --- a/PBGitGrapher.h +++ b/Classes/git/PBGitGrapher.h @@ -6,17 +6,12 @@ // Copyright 2008 __MyCompanyName__. All rights reserved. // -#import -#import "PBGitCommit.h" -#import "PBGitGraphLine.h" -#import "PBGraphCellInfo.h" +@class PBGitRepository; +@class PBGitCommit; -@interface PBGitGrapher : NSObject { - PBGraphCellInfo *previous; - void *pl; - int curLane; -} +@interface PBGitGrapher : NSObject - (id) initWithRepository:(PBGitRepository *)repo; - (void) decorateCommit:(PBGitCommit *)commit; + @end diff --git a/Classes/git/PBGitGrapher.mm b/Classes/git/PBGitGrapher.mm new file mode 100644 index 000000000..af2b4f5d1 --- /dev/null +++ b/Classes/git/PBGitGrapher.mm @@ -0,0 +1,215 @@ +// +// PBGitGrapher.m +// GitX +// +// Created by Pieter de Bie on 17-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#include +#include + +#import "PBGraphCellInfo.h" +#import "PBGitGrapher.h" +#import "PBGitCommit.h" +#import "PBGitLane.h" +#import "PBGitGraphLine.h" + +#import +#import +#include +#import + +using namespace std; +typedef std::vector LaneCollection; + +@interface PBGitGrapher () + +@property (nonatomic, strong) PBGraphCellInfo *previous; +@property (nonatomic, assign) LaneCollection *pl; +@property (nonatomic, assign) int curLane; +@property (nonatomic, assign) int laneIndex; + +@end + +@implementation PBGitGrapher + +- (id) initWithRepository: (PBGitRepository*) repo +{ + self = [super init]; + if (!self) { + return nil; + } + + self.pl = new LaneCollection; + + return self; +} + +int long_to_integer_bound(long input, int min, int max) +{ + if (input < (long)min) { + return min; + } else if (input > (long)max) { + return max; + } + return (int)input; +} + +void add_line(struct PBGitGraphLine *lines, int *nLines, int upper, long from, long to, int index) +{ + // TODO: put in one thing + struct PBGitGraphLine a = { + long_to_integer_bound(upper, 0, 1), + long_to_integer_bound(from, INT8_MIN, INT8_MAX), + long_to_integer_bound(to, INT8_MIN, INT8_MAX), + index + }; + lines[(*nLines)++] = a; +} + +- (void) decorateCommit: (PBGitCommit *) commit +{ + int i = 0; + long newPos = -1; + LaneCollection *currentLanes = new LaneCollection; + LaneCollection *previousLanes = self.pl; + NSArray *parents = [commit parents]; + NSUInteger nParents = [parents count]; + + unsigned long maxLines = (previousLanes->size() + nParents + 2) * 2; + struct PBGitGraphLine *lines = (struct PBGitGraphLine *)calloc(maxLines, sizeof(struct PBGitGraphLine)); + int currentLine = 0; + + PBGitLane *currentLane = NULL; + BOOL didFirst = NO; + const git_oid *commit_oid = commit.OID.git_oid; + + // First, iterate over earlier columns and pass through any that don't want this commit + if (self.previous != nil) { + // We can't count until numColumns here, as it's only used for the width of the cell. + LaneCollection::iterator it = previousLanes->begin(); + for (; it != previousLanes->end(); ++it) { + i++; + if (!*it) // This is an empty lane, created when the lane previously had a parentless(root) commit + continue; + + // This is our commit! We should do a "merge": move the line from + // our upperMapping to their lowerMapping + if ((*it)->isCommit(commit_oid)) { + if (!didFirst) { + didFirst = YES; + currentLanes->push_back(*it); + currentLane = currentLanes->back(); + newPos = currentLanes->size(); + add_line(lines, ¤tLine, 1, i, newPos, (*it)->index()); + if (nParents) + add_line(lines, ¤tLine, 0, newPos, newPos, (*it)->index()); + } + else { + add_line(lines, ¤tLine, 1, i, newPos, (*it)->index()); + delete *it; + } + } + else { + // We are not this commit. + currentLanes->push_back(*it); + add_line(lines, ¤tLine, 1, i, currentLanes->size(), (*it)->index()); + add_line(lines, ¤tLine, 0, currentLanes->size(), currentLanes->size(), (*it)->index()); + } + // For existing columns, we always just continue straight down + // ^^ I don't know what that means anymore :( + } + } + //Add your own parents + + // If we already did the first parent, don't do so again + if (!didFirst && nParents) { + const git_oid *parentOID = [(GTOID*)[parents objectAtIndex:0] git_oid]; + PBGitLane *newLane = new PBGitLane(_laneIndex++, parentOID); + currentLanes->push_back(newLane); + newPos = currentLanes->size(); + add_line(lines, ¤tLine, 0, newPos, newPos, newLane->index()); + } + + // Add all other parents + + // If we add at least one parent, we can go back a single column. + // This boolean will tell us if that happened + BOOL addedParent = NO; + + int parentIndex = 0; + for (parentIndex = 1; parentIndex < nParents; ++parentIndex) { + const git_oid *parentOID = [(GTOID*)[parents objectAtIndex:parentIndex] git_oid]; + int i = 0; + BOOL was_displayed = NO; + LaneCollection::iterator it = currentLanes->begin(); + for (; it != currentLanes->end(); ++it) { + i++; + if ((*it)->isCommit(parentOID)) { + add_line(lines, ¤tLine, 0, i, newPos,(*it)->index()); + was_displayed = YES; + break; + } + } + if (was_displayed) + continue; + + // Really add this parent + addedParent = YES; + PBGitLane *newLane = new PBGitLane(_laneIndex++, parentOID); + currentLanes->push_back(newLane); + add_line(lines, ¤tLine, 0, currentLanes->size(), newPos, newLane->index()); + } + + if (commit.lineInfo) { + self.previous = commit.lineInfo; + self.previous.position = newPos; + self.previous.lines = lines; + } + else + self.previous = [[PBGraphCellInfo alloc] initWithPosition:newPos andLines:lines]; + + if (currentLine > maxLines) + NSLog(@"Number of lines: %i vs allocated: %lu", currentLine, maxLines); + + self.previous.nLines = currentLine; + + // If a parent was added, we have room to not indent. + if (addedParent) + self.previous.numColumns = currentLanes->size() - 1; + else + self.previous.numColumns = currentLanes->size(); + + // Update the current lane to point to the new parent + if (currentLane) { + if (nParents > 0) + currentLane->setSha( [(GTOID*)[parents objectAtIndex:0] git_oid]); + else { + // The current lane's commit does not have any parents + // AKA, this is a first commit + // Empty the entry and free the lane. + // We empty the lane in the case of a subtree merge, where + // multiple first commits can be present. By emptying the lane, + // we allow room to create a nice merge line. + std::replace(currentLanes->begin(), currentLanes->end(), currentLane, (PBGitLane *)0); + delete currentLane; + } + } + + delete previousLanes; + + self.pl = currentLanes; + commit.lineInfo = self.previous; +} + +- (void) dealloc +{ + LaneCollection *lanes = self.pl; + LaneCollection::iterator it = lanes->begin(); + for (; it != lanes->end(); ++it) + delete *it; + + delete lanes; +} +@end diff --git a/Classes/git/PBGitHistoryGrapher.h b/Classes/git/PBGitHistoryGrapher.h new file mode 100644 index 000000000..d655a5e5b --- /dev/null +++ b/Classes/git/PBGitHistoryGrapher.h @@ -0,0 +1,31 @@ +// +// PBGitHistoryGrapher.h +// GitX +// +// Created by Nathan Kinsinger on 2/20/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +#define kCurrentQueueKey @"kCurrentQueueKey" +#define kNewCommitsKey @"kNewCommitsKey" + + +@class PBGitGrapher; + + +@interface PBGitHistoryGrapher : NSObject { + __weak id delegate; + NSOperationQueue *currentQueue; + + NSMutableSet *searchOIDs; + PBGitGrapher *grapher; + BOOL viewAllBranches; +} + +- (id) initWithBaseCommits:(NSSet *)commits viewAllBranches:(BOOL)viewAll queue:(NSOperationQueue *)queue delegate:(id)theDelegate; +- (void) graphCommits:(NSArray *)revList; + +@end diff --git a/Classes/git/PBGitHistoryGrapher.m b/Classes/git/PBGitHistoryGrapher.m new file mode 100644 index 000000000..a3d31ac1f --- /dev/null +++ b/Classes/git/PBGitHistoryGrapher.m @@ -0,0 +1,78 @@ +// +// PBGitHistoryGrapher.m +// GitX +// +// Created by Nathan Kinsinger on 2/20/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitHistoryGrapher.h" +#import "PBGitGrapher.h" +#import "PBGitCommit.h" + +@implementation PBGitHistoryGrapher + + +- (id) initWithBaseCommits:(NSSet *)commits viewAllBranches:(BOOL)viewAll queue:(NSOperationQueue *)queue delegate:(id)theDelegate +{ + self = [super init]; + + delegate = theDelegate; + currentQueue = queue; + searchOIDs = [NSMutableSet setWithSet:commits]; + grapher = [[PBGitGrapher alloc] initWithRepository:nil]; + viewAllBranches = viewAll; + + return self; +} + + +- (void)sendCommits:(NSArray *)commits +{ + NSDictionary *commitData = [NSDictionary dictionaryWithObjectsAndKeys:currentQueue, kCurrentQueueKey, commits, kNewCommitsKey, nil]; + id strongDelegate = delegate; + [strongDelegate performSelectorOnMainThread:@selector(updateCommitsFromGrapher:) withObject:commitData waitUntilDone:NO]; +} + + +- (void) graphCommits:(NSArray *)revList +{ + if (!revList || [revList count] == 0) + return; + + id strongDelegate = delegate; + //NSDate *start = [NSDate date]; + NSThread *currentThread = [NSThread currentThread]; + NSDate *lastUpdate = [NSDate date]; + NSMutableArray *commits = [NSMutableArray array]; + NSInteger counter = 0; + + for (PBGitCommit *commit in revList) { + if ([currentThread isCancelled]) + return; + GTOID *commitOID = commit.OID; + if (viewAllBranches || [searchOIDs containsObject:commitOID]) { + [grapher decorateCommit:commit]; + [commits addObject:commit]; + if (!viewAllBranches) { + [searchOIDs removeObject:commitOID]; + [searchOIDs addObjectsFromArray:[commit parents]]; + } + } + if (++counter % 100 == 0) { + if ([[NSDate date] timeIntervalSinceDate:lastUpdate] > 0.5) { + [self sendCommits:commits]; + commits = [NSMutableArray array]; + lastUpdate = [NSDate date]; + } + } + } + //NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start]; + //NSLog(@"Graphed %i commits in %f seconds (%f/sec)", counter, duration, counter/duration); + + [self sendCommits:commits]; + [strongDelegate performSelectorOnMainThread:@selector(finishedGraphing) withObject:nil waitUntilDone:NO]; +} + + +@end diff --git a/Classes/git/PBGitHistoryList.h b/Classes/git/PBGitHistoryList.h new file mode 100644 index 000000000..66e21f1fd --- /dev/null +++ b/Classes/git/PBGitHistoryList.h @@ -0,0 +1,52 @@ +// +// PBGitHistoryList.h +// GitX +// +// Created by Nathan Kinsinger on 2/20/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@class PBGitRepository; +@class PBGitRevSpecifier; +@class PBGitRef; +@class PBGitRevList; +@class PBGitHistoryGrapher; +@class GTOID; + +@interface PBGitHistoryList : NSObject { + __weak PBGitRepository *repository; + + PBGitRevList *projectRevList; + PBGitRevList *currentRevList; + + GTOID *lastOID; + NSSet *lastRefOIDs; + NSInteger lastBranchFilter; + PBGitRef *lastRemoteRef; + BOOL resetCommits; + BOOL shouldReloadProjectHistory; + + PBGitHistoryGrapher *grapher; + NSOperationQueue *graphQueue; + + NSMutableArray *commits; + BOOL isUpdating; +} + +- (id) initWithRepository:(PBGitRepository *)repo; +- (void) forceUpdate; +- (void) updateHistory; +- (void)cleanup; + +- (void) updateCommitsFromGrapher:(NSDictionary *)commitData; + + +@property PBGitRevList *projectRevList; +@property NSMutableArray *commits; +@property (readonly) NSArray *projectCommits; +@property (assign) BOOL isUpdating; + +@end diff --git a/Classes/git/PBGitHistoryList.m b/Classes/git/PBGitHistoryList.m new file mode 100644 index 000000000..95ded5c2f --- /dev/null +++ b/Classes/git/PBGitHistoryList.m @@ -0,0 +1,371 @@ +// +// PBGitHistoryList.m +// GitX +// +// Created by Nathan Kinsinger on 2/20/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitHistoryList.h" +#import "PBGitRepository.h" +#import "PBGitRevList.h" +#import "PBGitGrapher.h" +#import "PBGitHistoryGrapher.h" +#import "PBGitRef.h" +#import "PBGitRevSpecifier.h" + +@interface PBGitHistoryList () + +- (void) resetGraphing; + +- (PBGitHistoryGrapher *) grapher; +- (NSInvocationOperation *) operationForCommits:(NSArray *)newCommits; + +- (void) updateProjectHistoryForRev:(PBGitRevSpecifier *)rev; +- (void) updateHistoryForRev:(PBGitRevSpecifier *)rev; + +@end + + + + +@implementation PBGitHistoryList + + +@synthesize projectRevList; +@synthesize commits; +@synthesize isUpdating; +@dynamic projectCommits; + + + +#pragma mark - +#pragma mark Public + +- (id) initWithRepository:(PBGitRepository *)repo +{ + self = [super init]; + if (!self) + return nil; + + commits = [NSMutableArray array]; + repository = repo; + lastBranchFilter = -1; + + shouldReloadProjectHistory = YES; + projectRevList = [[PBGitRevList alloc] initWithRepository:repository rev:[PBGitRevSpecifier allBranchesRevSpec] shouldGraph:NO]; + + return self; +} + +- (void)dealloc { + [self cleanup]; +} + +- (void) forceUpdate +{ + if ([repository.currentBranch isSimpleRef]) + shouldReloadProjectHistory = YES; + + [self updateHistory]; +} + + +- (void) updateHistory +{ + PBGitRevSpecifier *rev = repository.currentBranch; + if (!rev) + return; + + if ([rev isSimpleRef]) + [self updateProjectHistoryForRev:rev]; + else + [self updateHistoryForRev:rev]; +} + + +- (void)cleanup +{ + if (currentRevList) { + [currentRevList removeObserver:self forKeyPath:@"commits"]; + [currentRevList cancel]; + currentRevList = nil; + } + [graphQueue cancelAllOperations]; + +} + + +- (NSArray *) projectCommits +{ + return [projectRevList.commits copy]; +} + + + +#pragma mark - +#pragma mark History Grapher delegate methods + +- (void) addCommitsFromArray:(NSArray *)array +{ + if (!array || [array count] == 0) + return; + + if (resetCommits) { + self.commits = [NSMutableArray array]; + resetCommits = NO; + } + + NSRange range = NSMakeRange([commits count], [array count]); + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:range]; + + [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"commits"]; + [commits addObjectsFromArray:array]; + [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"commits"]; +} + + +- (void) updateCommitsFromGrapher:(NSDictionary *)commitData +{ + if ([commitData objectForKey:kCurrentQueueKey] != graphQueue) + return; + + [self addCommitsFromArray:[commitData objectForKey:kNewCommitsKey]]; +} + +- (void) finishedGraphing +{ + if (!currentRevList.parsing && ([[graphQueue operations] count] == 0)) { + self.isUpdating = NO; + } +} + + + +#pragma mark - +#pragma mark Private + +- (void) resetGraphing +{ + resetCommits = YES; + self.isUpdating = YES; + + [graphQueue cancelAllOperations]; + graphQueue = [[NSOperationQueue alloc] init]; + [graphQueue setMaxConcurrentOperationCount:1]; + + grapher = [self grapher]; +} + + +- (NSInvocationOperation *) operationForCommits:(NSArray *)newCommits +{ + return [[NSInvocationOperation alloc] initWithTarget:grapher selector:@selector(graphCommits:) object:newCommits]; +} + + +- (NSSet *) baseCommitsForLocalRefs +{ + NSMutableSet *baseCommitOIDs = [NSMutableSet set]; + NSDictionary *refs = repository.refs; + + for (GTOID *OID in refs) + for (PBGitRef *ref in [refs objectForKey:OID]) + if ([ref isBranch] || [ref isTag]) + [baseCommitOIDs addObject:OID]; + + if (![[PBGitRef refFromString:[[repository headRef] simpleRef]] type]) + [baseCommitOIDs addObject:repository.headOID]; + + return baseCommitOIDs; +} + + +- (NSSet *) baseCommitsForRemoteRefs +{ + NSMutableSet *baseCommitOIDs = [NSMutableSet set]; + NSDictionary *refs = repository.refs; + + PBGitRef *remoteRef = [[repository.currentBranch ref] remoteRef]; + + for (GTOID *OID in refs) + for (PBGitRef *ref in [refs objectForKey:OID]) + if ([remoteRef isEqualToRef:[ref remoteRef]]) + [baseCommitOIDs addObject:OID]; + + return baseCommitOIDs; +} + + +- (NSSet *) baseCommits +{ + if ((repository.currentBranchFilter == kGitXSelectedBranchFilter) || (repository.currentBranchFilter == kGitXAllBranchesFilter)) { + if (lastOID) + return [NSMutableSet setWithObject:lastOID]; + else if ([repository.currentBranch isSimpleRef]) { + PBGitRef *currentRef = [repository.currentBranch ref]; + GTOID *OID = [repository OIDForRef:currentRef]; + if (OID) + return [NSMutableSet setWithObject:OID]; + } + } + else if (repository.currentBranchFilter == kGitXLocalRemoteBranchesFilter) { + if ([[repository.currentBranch ref] isRemote]) + return [self baseCommitsForRemoteRefs]; + else + return [self baseCommitsForLocalRefs]; + } + + return [NSMutableSet set]; +} + + +- (PBGitHistoryGrapher *) grapher +{ + BOOL viewAllBranches = (repository.currentBranchFilter == kGitXAllBranchesFilter); + + return [[PBGitHistoryGrapher alloc] initWithBaseCommits:[self baseCommits] viewAllBranches:viewAllBranches queue:graphQueue delegate:self]; +} + + +- (void) setCurrentRevList:(PBGitRevList *)parser +{ + if (currentRevList == parser) + return; + + if (currentRevList) + [currentRevList removeObserver:self forKeyPath:@"commits"]; + + currentRevList = parser; + + [currentRevList addObserver:self forKeyPath:@"commits" options:NSKeyValueObservingOptionNew context:@"commitsUpdated"]; +} + + +- (BOOL) isAllBranchesOnlyUpdate +{ + return (lastBranchFilter == kGitXAllBranchesFilter) && (repository.currentBranchFilter == kGitXAllBranchesFilter); +} + + +- (BOOL) isLocalRemoteOnlyUpdate:(PBGitRevSpecifier *)rev +{ + if ((lastBranchFilter == kGitXLocalRemoteBranchesFilter) && (repository.currentBranchFilter == kGitXLocalRemoteBranchesFilter)) { + if (!lastRemoteRef && ![[rev ref] isRemote]) + return YES; + + if ([lastRemoteRef isEqualToRef:[[rev ref] remoteRef]]) + return YES; + } + + return NO; +} + + +- (BOOL) selectedBranchNeedsNewGraph:(PBGitRevSpecifier *)rev +{ + if (![rev isSimpleRef]) + return YES; + + if ([self isAllBranchesOnlyUpdate] || [self isLocalRemoteOnlyUpdate:rev]) { + lastRemoteRef = [[rev ref] remoteRef]; + lastOID = nil; + self.isUpdating = NO; + return NO; + } + + GTOID *revOID = [repository OIDForRef:[rev ref]]; + if ([revOID isEqual:lastOID] && (lastBranchFilter == repository.currentBranchFilter)) + return NO; + + lastBranchFilter = repository.currentBranchFilter; + lastRemoteRef = [[rev ref] remoteRef]; + lastOID = revOID; + + return YES; +} + + +- (BOOL) haveRefsBeenModified +{ + [repository reloadRefs]; + + NSMutableSet *currentRefOIDs = [NSMutableSet setWithArray:[repository.refs allKeys]]; + [currentRefOIDs minusSet:lastRefOIDs]; + lastRefOIDs = [NSSet setWithArray:[repository.refs allKeys]]; + + return [currentRefOIDs count] != 0; +} + + +#pragma mark updating history + +- (void) updateProjectHistoryForRev:(PBGitRevSpecifier *)rev +{ + [self setCurrentRevList:projectRevList]; + + if ([self haveRefsBeenModified]) + shouldReloadProjectHistory = YES; + + if (![self selectedBranchNeedsNewGraph:rev] && !shouldReloadProjectHistory) + return; + + [self resetGraphing]; + + if (shouldReloadProjectHistory) { + shouldReloadProjectHistory = NO; + lastBranchFilter = -1; + lastRemoteRef = nil; + lastOID = nil; + self.commits = [NSMutableArray array]; + [projectRevList loadRevisonsWithCompletionBlock:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self finishedGraphing]; + }); + }]; + } else { + [graphQueue addOperation:[self operationForCommits:projectRevList.commits]]; + } +} + + +- (void) updateHistoryForRev:(PBGitRevSpecifier *)rev +{ + PBGitRevList *otherRevListParser = [[PBGitRevList alloc] initWithRepository:repository rev:rev shouldGraph:YES]; + + [self setCurrentRevList:otherRevListParser]; + [self resetGraphing]; + lastBranchFilter = -1; + lastRemoteRef = nil; + lastOID = nil; + self.commits = [NSMutableArray array]; + + [otherRevListParser loadRevisonsWithCompletionBlock:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self finishedGraphing]; + }); + }]; +} + + + +#pragma mark - +#pragma mark Key Value Observing +- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([@"commitsUpdated" isEqualToString:(__bridge NSString*)context]) { + NSInteger changeKind = [(NSNumber *)[change objectForKey:NSKeyValueChangeKindKey] intValue]; + if (changeKind == NSKeyValueChangeInsertion) { + NSArray *newCommits = [change objectForKey:NSKeyValueChangeNewKey]; + if ([repository.currentBranch isSimpleRef]) + [graphQueue addOperation:[self operationForCommits:newCommits]]; + else + [self addCommitsFromArray:newCommits]; + } + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + +@end diff --git a/PBGitIndex.h b/Classes/git/PBGitIndex.h similarity index 74% rename from PBGitIndex.h rename to Classes/git/PBGitIndex.h index 7ca1db78b..f3d712625 100644 --- a/PBGitIndex.h +++ b/Classes/git/PBGitIndex.h @@ -26,6 +26,7 @@ extern NSString *PBGitIndexIndexUpdated; // Committing files extern NSString *PBGitIndexCommitStatus; extern NSString *PBGitIndexCommitFailed; +extern NSString *PBGitIndexCommitHookFailed; extern NSString *PBGitIndexFinishedCommit; // Changing to amend @@ -40,23 +41,14 @@ extern NSString *PBGitIndexOperationFailed; // As a single git repository can have multiple trees, // the tree has to be given explicitly, even though // multiple trees is not yet supported in GitX -@interface PBGitIndex : NSObject { - -@private - PBGitRepository *repository; - NSURL *workingDirectory; - NSMutableArray *files; - - NSUInteger refreshStatus; - NSDictionary *amendEnvironment; - BOOL amend; -} +@interface PBGitIndex : NSObject // Whether we want the changes for amending, -// or for -@property BOOL amend; +// or for making a new commit. +@property (assign, getter=isAmend) BOOL amend; +@property (weak, readonly) PBGitRepository *repository; -- (id)initWithRepository:(PBGitRepository *)repository workingDirectory:(NSURL *)workingDirectory; +- (id)initWithRepository:(PBGitRepository *)repository; // A list of PBChangedFile's with differences between the work tree and the index // This method is KVO-aware, so changes when any of the index-modifying methods are called @@ -66,12 +58,12 @@ extern NSString *PBGitIndexOperationFailed; // Refresh the index - (void)refresh; -- (void)commitWithMessage:(NSString *)commitMessage; +- (void)commitWithMessage:(NSString *)commitMessage andVerify:(BOOL) doVerify; // Inter-file changes: -- (BOOL)stageFiles:(NSArray *)stageFiles; -- (BOOL)unstageFiles:(NSArray *)unstageFiles; -- (void)discardChangesForFiles:(NSArray *)discardFiles; +- (BOOL)stageFiles:(NSArray *)stageFiles; +- (BOOL)unstageFiles:(NSArray *)unstageFiles; +- (void)discardChangesForFiles:(NSArray *)discardFiles; // Intra-file changes - (BOOL)applyPatch:(NSString *)hunk stage:(BOOL)stage reverse:(BOOL)reverse; diff --git a/Classes/git/PBGitIndex.m b/Classes/git/PBGitIndex.m new file mode 100644 index 000000000..c114b3579 --- /dev/null +++ b/Classes/git/PBGitIndex.m @@ -0,0 +1,702 @@ +// +// PBGitIndex.m +// GitX +// +// Created by Pieter de Bie on 9/12/09. +// Copyright 2009 Pieter de Bie. All rights reserved. +// + +#import "PBGitIndex.h" +#import "PBGitRepository.h" +#import "PBGitRepository_PBGitBinarySupport.h" +#import "PBGitBinary.h" +#import "PBTask.h" +#import "PBChangedFile.h" + +NSString *PBGitIndexIndexRefreshStatus = @"PBGitIndexIndexRefreshStatus"; +NSString *PBGitIndexIndexRefreshFailed = @"PBGitIndexIndexRefreshFailed"; +NSString *PBGitIndexFinishedIndexRefresh = @"PBGitIndexFinishedIndexRefresh"; + +NSString *PBGitIndexIndexUpdated = @"PBGitIndexIndexUpdated"; + +NSString *PBGitIndexCommitStatus = @"PBGitIndexCommitStatus"; +NSString *PBGitIndexCommitFailed = @"PBGitIndexCommitFailed"; +NSString *PBGitIndexCommitHookFailed = @"PBGitIndexCommitHookFailed"; +NSString *PBGitIndexFinishedCommit = @"PBGitIndexFinishedCommit"; + +NSString *PBGitIndexAmendMessageAvailable = @"PBGitIndexAmendMessageAvailable"; +NSString *PBGitIndexOperationFailed = @"PBGitIndexOperationFailed"; + +NS_ENUM(NSUInteger, PBGitIndexOperation) { + PBGitIndexStageFiles, + PBGitIndexUnstageFiles, +}; + +@interface PBGitIndex (IndexRefreshMethods) + +- (NSMutableDictionary *)dictionaryForLines:(NSArray *)lines; +- (void)addFilesFromDictionary:(NSMutableDictionary *)dictionary staged:(BOOL)staged tracked:(BOOL)tracked; + +- (NSArray *)linesFromData:(NSData *)data; + +@end + +@interface PBGitIndex () { + dispatch_queue_t _indexRefreshQueue; + dispatch_group_t _indexRefreshGroup; + BOOL _amend; +} + +@property (retain) NSDictionary *amendEnvironment; +@property (retain) NSMutableArray *files; +@end + +@implementation PBGitIndex + +- (id)initWithRepository:(PBGitRepository *)theRepository +{ + if (!(self = [super init])) + return nil; + + NSAssert(theRepository, @"PBGitIndex requires a repository"); + + _repository = theRepository; + + _files = [NSMutableArray array]; + + _indexRefreshGroup = dispatch_group_create(); + + return self; +} + +- (NSArray *)indexChanges +{ + return self.files; +} + +- (void)setAmend:(BOOL)newAmend +{ + if (newAmend == _amend) + return; + + _amend = newAmend; + self.amendEnvironment = nil; + + [self refresh]; + + if (!newAmend) + return; + + // If we amend, we want to keep the author information for the previous commit + // We do this by reading in the previous commit, and storing the information + // in a dictionary. This dictionary will then later be read by [self commit:] + GTReference *headRef = [self.repository.gtRepo headReferenceWithError:NULL]; + GTCommit *commit = [headRef resolvedTarget]; + if (commit) + self.amendEnvironment = @{ + @"GIT_AUTHOR_NAME": commit.author.name, + @"GIT_AUTHOR_EMAIL": commit.author.email, + @"GIT_AUTHOR_DATE": commit.commitDate, + }; + + NSDictionary *notifDict = nil; + if (commit.message) { + notifDict = @{@"message": commit.message}; + } + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexAmendMessageAvailable + object:self + userInfo:notifDict]; +} + +- (BOOL)isAmend +{ + return _amend; +} + + +- (void)postIndexRefreshFinished { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexFinishedIndexRefresh object:self]; + }); +} + +// A multi-purpose notification sender for a refresh operation +// TODO: make -refresh take a completion handler, an NSError or *anything else* +- (void)postIndexRefreshSuccess:(BOOL)success message:(nullable NSString *)message { + dispatch_async(dispatch_get_main_queue(), ^{ + if (!success) { + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexIndexRefreshFailed + object:self + userInfo:@{@"description": message}]; + } else { + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexIndexRefreshStatus + object:self + userInfo:@{@"description": message}]; + } + }); + + [self postIndexUpdated]; +} + +- (void)postIndexUpdated { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexIndexUpdated object:self]; + }); +} + +- (void)refresh +{ + dispatch_group_enter(_indexRefreshGroup); + + // Ask Git to refresh the index + [PBTask launchTask:[PBGitBinary path] + arguments:@[@"update-index", @"-q", @"--unmerged", @"--ignore-missing", @"--refresh"] + inDirectory:self.repository.workingDirectoryURL.path + completionHandler:^(NSData *readData, NSError *error) { + if (error) { + [self postIndexRefreshSuccess:NO message:@"update-index failed"]; + } else { + [self postIndexRefreshSuccess:YES message:@"update-index success"]; + } + + dispatch_group_leave(self->_indexRefreshGroup); + }]; + + + // This block is called when each of the other blocks scheduled are done, + // which means we can delete all files previously marked as deletable. + // Note, there are scheduled blocks *below* this one ;-). + dispatch_group_notify(_indexRefreshGroup, dispatch_get_main_queue(), ^{ + + // At this point, all index operations have finished. + // We need to find all files that don't have either + // staged or unstaged files, and delete them + + NSMutableArray *deleteFiles = [NSMutableArray array]; + for (PBChangedFile *file in self.files) { + if (!file.hasStagedChanges && !file.hasUnstagedChanges) + [deleteFiles addObject:file]; + } + + if ([deleteFiles count]) { + [self willChangeValueForKey:@"indexChanges"]; + for (PBChangedFile *file in deleteFiles) + [self.files removeObject:file]; + [self didChangeValueForKey:@"indexChanges"]; + } + + [self postIndexRefreshFinished]; + }); + + if ([self.repository isBareRepository]) + { + return; + } + + // Other files + dispatch_group_enter(_indexRefreshGroup); + [PBTask launchTask:[PBGitBinary path] + arguments:@[@"ls-files", @"--others", @"--exclude-standard", @"-z"] + inDirectory:self.repository.workingDirectoryURL.path + completionHandler:^(NSData *readData, NSError *error) { + if (error) { + [self postIndexRefreshSuccess:NO message:@"ls-files failed"]; + } else { + NSArray *lines = [self linesFromData:readData]; + NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:[lines count]]; + // Other files are untracked, so we don't have any real index information. Instead, we can just fake it. + // The line below is not used at all, as for these files the commitBlob isn't set + NSArray *fileStatus = [NSArray arrayWithObjects:@":000000", @"100644", @"0000000000000000000000000000000000000000", @"0000000000000000000000000000000000000000", @"A", nil]; + for (NSString *path in lines) { + if ([path length] == 0) + continue; + [dictionary setObject:fileStatus forKey:path]; + } + + [self addFilesFromDictionary:dictionary staged:NO tracked:NO]; + } + + [self postIndexRefreshSuccess:YES message:@"ls-files success"]; + dispatch_group_leave(self->_indexRefreshGroup); + }]; + + // Staged files + dispatch_group_enter(_indexRefreshGroup); + [PBTask launchTask:[PBGitBinary path] + arguments:@[@"diff-index", @"--cached", @"-z", [self parentTree]] + inDirectory:self.repository.workingDirectoryURL.path + completionHandler:^(NSData *readData, NSError *error) { + if (error) { + [self postIndexRefreshSuccess:NO message:@"diff-index failed"]; + } else { + NSArray *lines = [self linesFromData:readData]; + NSMutableDictionary *dic = [self dictionaryForLines:lines]; + [self addFilesFromDictionary:dic staged:YES tracked:YES]; + } + + [self postIndexRefreshSuccess:YES message:@"diff-index success"]; + + dispatch_group_leave(self->_indexRefreshGroup); + }]; + + + // Unstaged files + dispatch_group_enter(_indexRefreshGroup); + [PBTask launchTask:[PBGitBinary path] + arguments:@[@"diff-files", @"-z"] + inDirectory:self.repository.workingDirectoryURL.path + completionHandler:^(NSData *readData, NSError *error) { + if (error) { + [self postIndexRefreshSuccess:NO message:@"diff-files failed"]; + } else { + NSArray *lines = [self linesFromData:readData]; + NSMutableDictionary *dic = [self dictionaryForLines:lines]; + [self addFilesFromDictionary:dic staged:NO tracked:YES]; + } + [self postIndexRefreshSuccess:YES message:@"diff-files success"]; + + dispatch_group_leave(self->_indexRefreshGroup); + }]; +} + +// Returns the tree to compare the index to, based +// on whether amend is set or not. +- (NSString *) parentTree +{ + NSString *parent = self.amend ? @"HEAD^" : @"HEAD"; + + if (![self.repository revisionExists:parent]) + // We don't have a head ref. Return the empty tree. + return @"4b825dc642cb6eb9a060e54bf8d69288fbee4904"; + + return parent; +} + +// TODO: make Asynchronous +- (void)commitWithMessage:(NSString *)commitMessage andVerify:(BOOL) doVerify +{ + NSMutableString *commitSubject = [@"commit: " mutableCopy]; + NSRange newLine = [commitMessage rangeOfString:@"\n"]; + if (newLine.location == NSNotFound) + [commitSubject appendString:commitMessage]; + else + [commitSubject appendString:[commitMessage substringToIndex:newLine.location]]; + + NSString *commitMessageFile; + commitMessageFile = [self.repository.gitURL.path stringByAppendingPathComponent:@"COMMIT_EDITMSG"]; + + [commitMessage writeToFile:commitMessageFile atomically:YES encoding:NSUTF8StringEncoding error:nil]; + + + [self postCommitUpdate:@"Creating tree"]; + NSString *tree = [self.repository outputForCommand:@"write-tree"]; + if ([tree length] != 40) + return [self postCommitFailure:@"Creating tree failed"]; + + + NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"commit-tree", tree, nil]; + NSString *parent = self.amend ? @"HEAD^" : @"HEAD"; + if ([self.repository revisionExists:parent]) { + [arguments addObject:@"-p"]; + [arguments addObject:parent]; + } + + [self postCommitUpdate:@"Creating commit"]; + int ret = 1; + + if (doVerify) { + [self postCommitUpdate:@"Running hooks"]; + NSString *hookFailureMessage = nil; + NSError *error = nil; + BOOL success = [self.repository executeHook:@"pre-commit" error:&error]; + if (!success) { + NSError *taskError = error.userInfo[NSUnderlyingErrorKey]; + NSString *hookOutput = taskError.userInfo[PBTaskTerminationOutputKey]; + hookFailureMessage = [NSString stringWithFormat:@"Pre-commit hook failed%@%@", + hookOutput && [hookOutput length] > 0 ? @":\n" : @"", + hookOutput]; + [self postCommitHookFailure:hookFailureMessage]; + return; + } + + success = [self.repository executeHook:@"commit-msg" arguments:@[commitMessageFile] error:&error]; + if (!success) { + NSError *taskError = error.userInfo[NSUnderlyingErrorKey]; + NSString *hookOutput = taskError.userInfo[PBTaskTerminationOutputKey]; + hookFailureMessage = [NSString stringWithFormat:@"Commit-msg hook failed%@%@", + hookOutput && [hookOutput length] > 0 ? @":\n" : @"", + hookOutput]; + [self postCommitHookFailure:hookFailureMessage]; + return; + } + } + + commitMessage = [NSString stringWithContentsOfFile:commitMessageFile encoding:NSUTF8StringEncoding error:nil]; + + NSString *commit = [self.repository outputForArguments:arguments + inputString:commitMessage + byExtendingEnvironment:self.amendEnvironment + retValue: &ret]; + + if (ret || [commit length] != 40) + return [self postCommitFailure:@"Could not create a commit object"]; + + [self postCommitUpdate:@"Updating HEAD"]; + [self.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-m", commitSubject, @"HEAD", commit, nil] + retValue: &ret]; + if (ret) + return [self postCommitFailure:@"Could not update HEAD"]; + + [self postCommitUpdate:@"Running post-commit hook"]; + + BOOL success = [self.repository executeHook:@"post-commit" error:NULL]; + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithBool:success] forKey:@"success"]; + NSString *description; + if (success) + description = [NSString stringWithFormat:@"Successfully created commit %@", commit]; + else + description = [NSString stringWithFormat:@"Post-commit hook failed, but successfully created commit %@", commit]; + + [userInfo setObject:description forKey:@"description"]; + [userInfo setObject:commit forKey:@"sha"]; + + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexFinishedCommit + object:self + userInfo:userInfo]; + if (!success) + return; + + self.repository.hasChanged = YES; + + self.amendEnvironment = nil; + if (self.amend) + self.amend = NO; + else + [self refresh]; + +} + +- (void)postCommitUpdate:(NSString *)update +{ + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexCommitStatus + object:self + userInfo:[NSDictionary dictionaryWithObject:update forKey:@"description"]]; +} + +- (void)postCommitFailure:(NSString *)reason +{ + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexCommitFailed + object:self + userInfo:[NSDictionary dictionaryWithObject:reason forKey:@"description"]]; +} + +- (void)postCommitHookFailure:(NSString *)reason +{ + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexCommitHookFailed + object:self + userInfo:[NSDictionary dictionaryWithObject:reason forKey:@"description"]]; +} + +- (void)postOperationFailed:(NSString *)description +{ + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexOperationFailed + object:self + userInfo:[NSDictionary dictionaryWithObject:description forKey:@"description"]]; +} + +- (BOOL)performStageOrUnstage:(BOOL)stage withFiles:(NSArray *)files +{ + // Do staging files by chunks of 1000 files each, to prevent program freeze (because NSPipe has limited capacity) + + NSUInteger filesCount = files.count; + const NSUInteger MAX_FILES_PER_STAGE = 1000; + + // Prepare first iteration + NSUInteger loopFrom = 0; + NSUInteger loopTo = MAX_FILES_PER_STAGE; + if (loopTo > filesCount) + loopTo = filesCount; + NSUInteger loopCount = 0; + + // Staging + while (loopCount < filesCount) { + // Input string for update-index + // This will be a list of filenames that + // should be updated. It's similar to + // "git add -- + NSMutableString *input = [NSMutableString string]; + + for (NSUInteger i = loopFrom; i < loopTo; i++) { + loopCount++; + + PBChangedFile *file = [files objectAtIndex:i]; + + if (stage) { + [input appendFormat:@"%@\0", file.path]; + } else { + NSString *indexInfo; + if (file.status == NEW) { + // Index info lies because the file is NEW + indexInfo = [NSString stringWithFormat:@"0 0000000000000000000000000000000000000000\t%@\0", file.path]; + } else { + indexInfo = [file indexInfo]; + } + [input appendString:indexInfo]; + } + } + + int ret = 1; + if (stage) { + [self.repository outputForArguments:[NSArray arrayWithObjects:@"update-index", @"--add", @"--remove", @"-z", @"--stdin", nil] + inputString:input + retValue:&ret]; + } else { + [self.repository outputForArguments:[NSArray arrayWithObjects:@"update-index", @"-z", @"--index-info", nil] + inputString:input + retValue:&ret]; + } + + if (ret) { + [self postOperationFailed:[NSString stringWithFormat:@"Error in %@ files. Return value: %i", (stage ? @"staging" : @"unstaging"), ret]]; + return NO; + } + + for (NSUInteger i = loopFrom; i < loopTo; i++) { + PBChangedFile *file = [files objectAtIndex:i]; + file.hasStagedChanges = stage; + file.hasUnstagedChanges = !stage; + } + + // Prepare next iteration + loopFrom = loopCount; + loopTo = loopFrom + MAX_FILES_PER_STAGE; + if (loopTo > filesCount) + loopTo = filesCount; + } + + [self postIndexUpdated]; + + return YES; +} + +- (BOOL)stageFiles:(NSArray *)stageFiles +{ + return [self performStageOrUnstage:YES withFiles:stageFiles]; +} + +- (BOOL)unstageFiles:(NSArray *)unstageFiles +{ + return [self performStageOrUnstage:NO withFiles:unstageFiles]; +} + +- (void)discardChangesForFiles:(NSArray *)discardFiles +{ + NSArray *paths = [discardFiles valueForKey:@"path"]; + NSString *input = [paths componentsJoinedByString:@"\0"]; + + NSArray *arguments = @[@"checkout-index", @"--index", @"--quiet", @"--force", @"-z", @"--stdin"]; + + PBTask *task = [PBTask taskWithLaunchPath:[PBGitBinary path] + arguments:arguments + inDirectory:self.repository.workingDirectoryURL.path]; + task.standardInputData = [input dataUsingEncoding:NSUTF8StringEncoding]; + + NSError *error = nil; + BOOL success = [task launchTask:&error]; + if (!success) { + [self postOperationFailed:[NSString stringWithFormat:@"Discarding changes failed with return value %@", error.userInfo[PBTaskTerminationStatusKey]]]; + return; + } + + for (PBChangedFile *file in discardFiles) + if (file.status != NEW) + file.hasUnstagedChanges = NO; + + [self postIndexUpdated]; +} + +- (BOOL)applyPatch:(NSString *)hunk stage:(BOOL)stage reverse:(BOOL)reverse; +{ + NSMutableArray *array = [NSMutableArray arrayWithObjects:@"apply", @"--unidiff-zero", nil]; + if (stage) + [array addObject:@"--cached"]; + if (reverse) + [array addObject:@"--reverse"]; + + int ret = 1; + NSString *error = [self.repository outputForArguments:array + inputString:hunk + retValue:&ret]; + + if (ret) { + [self postOperationFailed:[NSString stringWithFormat:@"Applying patch failed with return value %i. Error: %@", ret, error]]; + return NO; + } + + // TODO: Try to be smarter about what to refresh + [self refresh]; + return YES; +} + + +- (NSString *)diffForFile:(PBChangedFile *)file staged:(BOOL)staged contextLines:(NSUInteger)context +{ + NSString *parameter = [NSString stringWithFormat:@"-U%lu", context]; + if (staged) { + NSString *indexPath = [@":0:" stringByAppendingString:file.path]; + + if (file.status == NEW) + return [self.repository outputForArguments:[NSArray arrayWithObjects:@"show", indexPath, nil]]; + + return [self.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-index", parameter, @"--cached", [self parentTree], @"--", file.path, nil]]; + } + + // unstaged + if (file.status == NEW) { + NSStringEncoding encoding; + NSError *error = nil; + NSURL *fileURL = [self.repository.workingDirectoryURL URLByAppendingPathComponent:file.path]; + NSString *contents = [NSString stringWithContentsOfURL:fileURL + usedEncoding:&encoding + error:&error]; + if (error) + return nil; + + return contents; + } + + return [self.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-files", parameter, @"--", file.path, nil]]; +} + +# pragma mark WebKit Accessibility + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return NO; +} + +@end + +@implementation PBGitIndex (IndexRefreshMethods) + +- (void) addFilesFromDictionary:(NSMutableDictionary *)dictionary staged:(BOOL)staged tracked:(BOOL)tracked +{ + // Iterate over all existing files + for (PBChangedFile *file in self.files) { + NSArray *fileStatus = [dictionary objectForKey:file.path]; + // Object found, this is still a cached / uncached thing + if (fileStatus) { + if (tracked) { + NSString *mode = [[fileStatus objectAtIndex:0] substringFromIndex:1]; + NSString *sha = [fileStatus objectAtIndex:2]; + file.commitBlobSHA = sha; + file.commitBlobMode = mode; + + if (staged) + file.hasStagedChanges = YES; + else + file.hasUnstagedChanges = YES; + if ([[fileStatus objectAtIndex:4] isEqualToString:@"D"]) + file.status = DELETED; + } else { + // Untracked file, set status to NEW, only unstaged changes + file.hasStagedChanges = NO; + file.hasUnstagedChanges = YES; + file.status = NEW; + } + + // We handled this file, remove it from the dictionary + [dictionary removeObjectForKey:file.path]; + } else { + // Object not found in the dictionary, so let's reset its appropriate + // change (stage or untracked) if necessary. + + // Staged dictionary, so file does not have staged changes + if (staged) + file.hasStagedChanges = NO; + // Tracked file does not have unstaged changes, file is not new, + // so we can set it to No. (If it would be new, it would not + // be in this dictionary, but in the "other dictionary"). + else if (tracked && file.status != NEW) + file.hasUnstagedChanges = NO; + // Unstaged, untracked dictionary ("Other" files), and file + // is indicated as new (which would be untracked), so let's + // remove it + else if (!tracked && file.status == NEW && file.commitBlobSHA == nil) + file.hasUnstagedChanges = NO; + } + } + + // Do new files only if necessary + if (![[dictionary allKeys] count]) + return; + + // All entries left in the dictionary haven't been accounted for + // above, so we need to add them to the "files" array + [self willChangeValueForKey:@"indexChanges"]; + for (NSString *path in [dictionary allKeys]) { + NSArray *fileStatus = [dictionary objectForKey:path]; + + PBChangedFile *file = [[PBChangedFile alloc] initWithPath:path]; + if ([[fileStatus objectAtIndex:4] isEqualToString:@"D"]) + file.status = DELETED; + else if([[fileStatus objectAtIndex:0] isEqualToString:@":000000"]) + file.status = NEW; + else + file.status = MODIFIED; + + if (tracked) { + file.commitBlobMode = [[fileStatus objectAtIndex:0] substringFromIndex:1]; + file.commitBlobSHA = [fileStatus objectAtIndex:2]; + } + + file.hasStagedChanges = staged; + file.hasUnstagedChanges = !staged; + + [self.files addObject:file]; + } + [self didChangeValueForKey:@"indexChanges"]; +} + +# pragma mark Utility methods +- (NSArray *)linesFromData:(NSData *)data +{ + if (!data) + return [NSArray array]; + + NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + // FIXME: throw an error? + if (!string) + return [NSArray array]; + + // Strip trailing null + if ([string hasSuffix:@"\0"]) + string = [string substringToIndex:[string length]-1]; + + if ([string length] == 0) + return [NSArray array]; + + return [string componentsSeparatedByString:@"\0"]; +} + +- (NSMutableDictionary *)dictionaryForLines:(NSArray *)lines +{ + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[lines count]/2]; + + // Fill the dictionary with the new information. These lines are in the form of: + // :00000 :0644 OTHER INDEX INFORMATION + // Filename + + NSAssert1([lines count] % 2 == 0, @"Lines must have an even number of lines: %@", lines); + + NSEnumerator *enumerator = [lines objectEnumerator]; + NSString *fileStatus; + while (fileStatus = [enumerator nextObject]) { + NSString *fileName = [enumerator nextObject]; + [dictionary setObject:[fileStatus componentsSeparatedByString:@" "] forKey:fileName]; + } + + return dictionary; +} + +@end diff --git a/PBGitLane.h b/Classes/git/PBGitLane.h similarity index 52% rename from PBGitLane.h rename to Classes/git/PBGitLane.h index 57d59c8d8..ec4ff2bca 100644 --- a/PBGitLane.h +++ b/Classes/git/PBGitLane.h @@ -5,40 +5,32 @@ // Created by Pieter de Bie on 27-08-08. // Copyright 2008 __MyCompanyName__. All rights reserved. // + #import -#include "git/oid.h" class PBGitLane { - static int s_colorIndex; - git_oid d_sha; int d_index; public: - PBGitLane(git_oid *sha) + PBGitLane(const git_oid *sha) { - d_index = s_colorIndex++; d_sha = *sha; } - PBGitLane(NSString *sha) - { - git_oid_mkstr(&d_sha, [sha UTF8String]); - d_index = s_colorIndex++; - } - - PBGitLane() + PBGitLane(int index, const git_oid *sha) + : d_index(index) { - d_index = s_colorIndex++; + git_oid_cpy(&d_sha, sha); } - bool isCommit(git_oid *sha) const + bool isCommit(const git_oid *sha) const { return !git_oid_cmp(&d_sha, sha); } - void setSha(git_oid sha); + void setSha(const git_oid *sha); git_oid const *sha() const { @@ -46,6 +38,4 @@ class PBGitLane { } int index() const; - - static void resetColors(); }; \ No newline at end of file diff --git a/Classes/git/PBGitLane.mm b/Classes/git/PBGitLane.mm new file mode 100644 index 000000000..d00fbd55f --- /dev/null +++ b/Classes/git/PBGitLane.mm @@ -0,0 +1,19 @@ +// +// PBGitLane.m +// GitX +// +// Created by Pieter de Bie on 27-08-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitLane.h" + +int PBGitLane::index() const +{ + return d_index; +} + +void PBGitLane::setSha(const git_oid *sha) +{ + d_sha = *sha; +} diff --git a/Classes/git/PBGitRef.h b/Classes/git/PBGitRef.h new file mode 100644 index 000000000..6497e929e --- /dev/null +++ b/Classes/git/PBGitRef.h @@ -0,0 +1,52 @@ +// +// PBGitRef.h +// GitX +// +// Created by Pieter de Bie on 06-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBGitRefish.h" + +extern NSString * const kGitXTagType; +extern NSString * const kGitXBranchType; +extern NSString * const kGitXRemoteType; +extern NSString * const kGitXRemoteBranchType; +extern NSString * const kGitXStashType; + +extern NSString * const kGitXTagRefPrefix; +extern NSString * const kGitXBranchRefPrefix; +extern NSString * const kGitXRemoteRefPrefix; +extern NSString * const kGitXStashRefPrefix; + + +@interface PBGitRef : NSObject + +// +- (NSString *) refishName; +- (NSString *) shortName; +- (NSString *) refishType; + +- (NSString *) tagName; +- (NSString *) branchName; +- (NSString *) remoteName; +- (NSString *) remoteBranchName; + +- (NSString *) type; +- (BOOL) isBranch; +- (BOOL) isTag; +- (BOOL) isRemote; +- (BOOL) isRemoteBranch; +- (BOOL) isStash; + +- (PBGitRef *) remoteRef; + +- (BOOL) isEqualToRef:(PBGitRef *)otherRef; + ++ (PBGitRef*) refFromString: (NSString*) s; +- (PBGitRef*) initWithString: (NSString*) s; + +@property(nonatomic, strong, readonly) NSString* ref; + +@end diff --git a/Classes/git/PBGitRef.m b/Classes/git/PBGitRef.m new file mode 100644 index 000000000..67c3f1ce2 --- /dev/null +++ b/Classes/git/PBGitRef.m @@ -0,0 +1,179 @@ +// +// PBGitRef.m +// GitX +// +// Created by Pieter de Bie on 06-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitRef.h" + + +NSString * const kGitXTagType = @"tag"; +NSString * const kGitXBranchType = @"branch"; +NSString * const kGitXRemoteType = @"remote"; +NSString * const kGitXRemoteBranchType = @"remote branch"; +NSString * const kGitXStashType = @"stash"; + +NSString * const kGitXTagRefPrefix = @"refs/tags/"; +NSString * const kGitXBranchRefPrefix = @"refs/heads/"; +NSString * const kGitXRemoteRefPrefix = @"refs/remotes/"; +NSString * const kGitXStashRefPrefix = @"refs/stash@"; + +@interface PBGitRef () + +@property(nonatomic, strong) NSString* ref; + +@end + +@implementation PBGitRef + +@synthesize ref; + +- (NSString *) tagName +{ + if (![self isTag]) + return nil; + + return [self shortName]; +} + +- (NSString *) branchName +{ + if (![self isBranch]) + return nil; + + return [self shortName]; +} + +- (NSString *) remoteName +{ + if (![self isRemote]) + return nil; + + return (NSString *)[[ref componentsSeparatedByString:@"/"] objectAtIndex:2]; +} + +- (NSString *) remoteBranchName +{ + if (![self isRemoteBranch]) + return nil; + + return [[self shortName] substringFromIndex:[[self remoteName] length] + 1];; +} + +- (NSString *) type +{ + if ([self isBranch]) + return @"head"; + if ([self isTag]) + return @"tag"; + if ([self isRemote]) + return @"remote"; + if ([self isStash]) + return @"stash"; + return nil; +} + +- (BOOL) isBranch +{ + return [ref hasPrefix:kGitXBranchRefPrefix]; +} + +- (BOOL) isTag +{ + return [ref hasPrefix:kGitXTagRefPrefix]; +} + +- (BOOL) isRemote +{ + return [ref hasPrefix:kGitXRemoteRefPrefix]; +} + +- (BOOL) isRemoteBranch +{ + if (![self isRemote]) + return NO; + + return ([[ref componentsSeparatedByString:@"/"] count] > 3); +} + +- (BOOL) isStash +{ + return [ref hasPrefix:kGitXStashRefPrefix]; +} + +- (BOOL) isEqualToRef:(PBGitRef *)otherRef +{ + return [ref isEqualToString:[otherRef ref]]; +} + +- (PBGitRef *) remoteRef +{ + if (![self isRemote]) + return nil; + + return [PBGitRef refFromString:[kGitXRemoteRefPrefix stringByAppendingString:[self remoteName]]]; +} + ++ (PBGitRef*) refFromString: (NSString*) s +{ + return [[PBGitRef alloc] initWithString:s]; +} + +- (PBGitRef*) initWithString: (NSString*) s +{ + self = [super init]; + if (!self) { + return nil; + } + ref = s; + return self; +} + +- (NSString *)debugDescription { + return [NSString stringWithFormat:@"<%@: %p ref: %@", NSStringFromClass([self class]), self, ref]; +} + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return NO; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name { + return NO; +} + + +#pragma mark + +- (NSString *) refishName +{ + return ref; +} + +- (NSString *) shortName +{ + if ([self isStash]) + return [ref substringFromIndex:5]; + if ([self type]) + return [ref substringFromIndex:[[self type] length] + 7]; + return ref; +} + +- (NSString *) refishType +{ + if ([self isBranch]) + return kGitXBranchType; + if ([self isTag]) + return kGitXTagType; + if ([self isRemoteBranch]) + return kGitXRemoteBranchType; + if ([self isRemote]) + return kGitXRemoteType; + if ([self isStash]) + return kGitXStashType; + return nil; +} + +@end diff --git a/Classes/git/PBGitRefish.h b/Classes/git/PBGitRefish.h new file mode 100644 index 000000000..ac0f9d84d --- /dev/null +++ b/Classes/git/PBGitRefish.h @@ -0,0 +1,27 @@ +// +// PBGitRefish.h +// GitX +// +// Created by Nathan Kinsinger on 12/25/09. +// Copyright 2009 Nathan Kinsinger. All rights reserved. +// + +#import + + +// Several git commands can take a ref "refs/heads/master" or an SHA. +// Use to accept a PBGitRef or a PBGitCommit without having to write +// two separate methods. +// +// refishName the full name of the ref "refs/heads/master" or the full SHA +// used in git commands +// shortName a more user friendly version of the refName, "master" or a short SHA +// refishType a short name for the type + +@protocol PBGitRefish + +- (NSString *) refishName; +- (NSString *) shortName; +- (NSString *) refishType; + +@end diff --git a/Classes/git/PBGitRepository.h b/Classes/git/PBGitRepository.h new file mode 100644 index 000000000..bb49ed80b --- /dev/null +++ b/Classes/git/PBGitRepository.h @@ -0,0 +1,131 @@ +// +// PBGitRepository.h +// GitTest +// +// Created by Pieter de Bie on 13-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + +@class PBGitHistoryList; +@class PBGitRevSpecifier; +@protocol PBGitRefish; +@class PBGitRef; +@class PBGitStash; +@class PBGitRepositoryDocument; +@class GTRepository; +@class GTConfiguration; + +extern NSString *PBGitRepositoryDocumentType; + +typedef enum branchFilterTypes { + kGitXAllBranchesFilter = 0, + kGitXLocalRemoteBranchesFilter, + kGitXSelectedBranchFilter +} PBGitXBranchFilterType; + +@class PBGitWindowController; +@class PBGitCommit; +@class PBGitIndex; +@class GTOID; +@class PBGitRepositoryWatcher; +@class GTSubmodule; + +@interface PBGitRepository : NSObject + +@property (nonatomic, weak) PBGitRepositoryDocument *document; // Backward-compatibility while PBGitRepository gets "modelized"; + +@property (nonatomic, assign) BOOL hasChanged; +@property (nonatomic, assign) NSInteger currentBranchFilter; + +@property (readonly, getter = getIndexURL) NSURL* indexURL; + +@property (nonatomic, strong) PBGitHistoryList *revisionList; +@property (nonatomic, readonly, strong) NSArray *stashes; +@property (nonatomic, readonly, strong) NSArray *branches; +@property (nonatomic, strong) NSMutableOrderedSet *branchesSet; +@property (nonatomic, strong) PBGitRevSpecifier *currentBranch; +@property (nonatomic, strong) NSMutableDictionary* refs; +@property (readonly, strong) GTRepository* gtRepo; +@property (nonatomic, readonly) BOOL isShallowRepository; + +@property (nonatomic, strong) NSMutableArray* submodules; +@property (readonly, strong) PBGitIndex *index; + +// Designated initializer +- (id)initWithURL:(NSURL *)repositoryURL error:(NSError **)error; + +- (BOOL) addRemote:(NSString *)remoteName withURL:(NSString *)URLString error:(NSError **)error; +- (BOOL) fetchRemoteForRef:(PBGitRef *)ref error:(NSError **)error; +- (BOOL) pullBranch:(PBGitRef *)branchRef fromRemote:(PBGitRef *)remoteRef rebase:(BOOL)rebase error:(NSError **)error; +- (BOOL) pushBranch:(PBGitRef *)branchRef toRemote:(PBGitRef *)remoteRef error:(NSError **)error; + +- (BOOL) checkoutRefish:(id )ref error:(NSError **)error; +- (BOOL) checkoutFiles:(NSArray *)files fromRefish:(id )ref error:(NSError **)error; +- (BOOL) mergeWithRefish:(id )ref error:(NSError **)error; +- (BOOL) cherryPickRefish:(id )ref error:(NSError **)error; +- (BOOL) rebaseBranch:(id )branch onRefish:(id )upstream error:(NSError **)error; +- (BOOL) createBranch:(NSString *)branchName atRefish:(id )ref error:(NSError **)error; +- (BOOL) createTag:(NSString *)tagName message:(NSString *)message atRefish:(id )commitSHA error:(NSError **)error; +- (BOOL) deleteRemote:(PBGitRef *)ref error:(NSError **)error; +- (BOOL) deleteRef:(PBGitRef *)ref error:(NSError **)error; + +- (BOOL) stashPop:(PBGitStash *)stash error:(NSError **)error; +- (BOOL) stashApply:(PBGitStash *)stash error:(NSError **)error; +- (BOOL) stashDrop:(PBGitStash *)stash error:(NSError **)error; +- (BOOL) stashSave:(NSError **)error; +- (BOOL) stashSaveWithKeepIndex:(BOOL)keepIndex error:(NSError **)error; + +- (BOOL)ignoreFilePaths:(NSArray *)filePaths error:(NSError **)error; + +- (BOOL)updateReference:(PBGitRef *)ref toPointAtCommit:(PBGitCommit *)newCommit; +- (NSString *)performDiff:(PBGitCommit *)startCommit against:(PBGitCommit *)diffCommit forFiles:(NSArray *)filePaths; + +- (NSURL *) gitURL ; + +- (BOOL)executeHook:(NSString *)name error:(NSError **)error; +- (BOOL)executeHook:(NSString *)name arguments:(NSArray *)arguments error:(NSError **)error; +- (BOOL)executeHook:(NSString *)name arguments:(NSArray *)arguments output:(NSString **)outputPtr error:(NSError **)error; + +- (NSString *)workingDirectory; +- (NSURL *)workingDirectoryURL; +- (NSString *)projectName; + +- (NSString *)gitIgnoreFilename; +- (BOOL)isBareRepository; + +- (BOOL)hasSVNRemote; + +- (void) reloadRefs; +- (void) lazyReload; +- (PBGitRevSpecifier*)headRef; +- (GTOID *)headOID; +- (PBGitCommit *)headCommit; +- (GTOID *)OIDForRef:(PBGitRef *)ref; +- (PBGitCommit *)commitForRef:(PBGitRef *)ref; +- (PBGitCommit *)commitForOID:(GTOID *)sha; +- (BOOL)isOIDOnSameBranch:(GTOID *)baseOID asOID:(GTOID *)testOID; +- (BOOL)isOIDOnHeadBranch:(GTOID *)testOID; +- (PBGitStash *)stashForRef:(PBGitRef *)ref; +- (BOOL)isRefOnHeadBranch:(PBGitRef *)testRef; +- (BOOL)checkRefFormat:(NSString *)refName; +- (BOOL)refExists:(PBGitRef *)ref; +- (PBGitRef *)refForName:(NSString *)name; + +- (NSArray *) remotes; +- (BOOL) hasRemotes; +- (PBGitRef *) remoteRefForBranch:(PBGitRef *)branch error:(NSError **)error; + +- (void) readCurrentBranch; +- (PBGitRevSpecifier*) addBranch: (PBGitRevSpecifier*) rev; +- (BOOL)removeBranch:(PBGitRevSpecifier *)rev; + +- (BOOL) revisionExists:(NSString*) spec; + +- (void) forceUpdateRevisions; +- (NSURL*) getIndexURL; + +- (GTSubmodule *)submoduleAtPath:(NSString *)path error:(NSError **)error; + +@end diff --git a/Classes/git/PBGitRepository.m b/Classes/git/PBGitRepository.m new file mode 100644 index 000000000..6e5bacaf2 --- /dev/null +++ b/Classes/git/PBGitRepository.m @@ -0,0 +1,1121 @@ +// +// PBGitRepository.m +// GitTest +// +// Created by Pieter de Bie on 13-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitRepository.h" + +#import "PBGitRepository_PBGitBinarySupport.h" +#import "PBGitCommit.h" +#import "PBGitIndex.h" +#import "PBGitWindowController.h" +#import "PBGitRepositoryDocument.h" +#import "PBGitBinary.h" + +#import "NSFileHandleExt.h" +#import "PBTask.h" +#import "PBGitRef.h" +#import "PBGitRevSpecifier.h" +#import "PBRemoteProgressSheet.h" +#import "PBGitRevList.h" +#import "PBGitDefaults.h" +#import "GitXScriptingConstants.h" +#import "PBHistorySearchController.h" +#import "PBGitRepositoryWatcher.h" +#import "PBRepositoryFinder.h" +#import "PBGitHistoryList.h" +#import "PBGitStash.h" +#import "PBError.h" + +@interface PBGitRepository () { + __strong PBGitRepositoryWatcher *watcher; + __strong PBGitRevSpecifier *_headRef; // Caching + __strong GTOID* _headOID; + __strong GTRepository* _gtRepo; + PBGitIndex *_index; +} + +@property (nonatomic, strong) NSNumber *hasSVNRepoConfig; + +@end + +@implementation PBGitRepository + +@synthesize revisionList, branchesSet, currentBranch, currentBranchFilter, hasChanged, refs; + +#pragma mark - +#pragma mark Memory management + +- (id)init +{ + self = [super init]; + if (!self) return nil; + + self.branchesSet = [NSMutableOrderedSet orderedSet]; + self.submodules = [NSMutableArray array]; + currentBranchFilter = [PBGitDefaults branchFilter]; + return self; +} + +- (id)initWithURL:(NSURL *)repositoryURL error:(NSError **)error +{ + self = [self init]; + if (!self) return nil; + + NSError *gtError = nil; + NSURL *repoURL = [PBRepositoryFinder gitDirForURL:repositoryURL]; + _gtRepo = [GTRepository repositoryWithURL:repoURL error:>Error]; + if (!_gtRepo) { + if (error) { + *error = [NSError pb_errorWithDescription:NSLocalizedString(@"Repository initialization failed", @"") + failureReason:[NSString stringWithFormat:NSLocalizedString(@"%@ does not appear to be a git repository.", @""), repositoryURL.path] + underlyingError:gtError]; + } + return nil; + } + + revisionList = [[PBGitHistoryList alloc] initWithRepository:self]; + + [self reloadRefs]; + + // Setup the FSEvents watcher to fire notifications when things change + watcher = [[PBGitRepositoryWatcher alloc] initWithRepository:self]; + + return self; +} + +- (void) dealloc +{ + // NSLog(@"Dealloc of repository"); + [watcher stop]; +} + +#pragma mark - +#pragma mark Properties/General methods + +- (NSURL *)getIndexURL +{ + NSError *error = nil; + GTIndex *index = [self.gtRepo indexWithError:&error]; + if (index == nil) { + NSLog(@"getIndexURL failed with error %@", error); + return nil; + } + NSURL* result = index.fileURL; + return result; +} + +- (BOOL)isBareRepository +{ + return self.gtRepo.isBare; +} + +- (BOOL)isShallowRepository +{ + // Using low-level function because GTRepository does not currently + // expose this information itself. + return (BOOL)git_repository_is_shallow(self.gtRepo.git_repository); +} + +- (BOOL)readHasSVNRemoteFromConfig +{ + NSError *error = nil; + GTConfiguration *config = [self.gtRepo configurationWithError:&error]; + NSArray *allKeys = config.configurationKeys; + for (NSString *key in allKeys) { + if ([key hasPrefix:@"svn-remote."]) { + return TRUE; + } + } + return false; +} + +- (BOOL)hasSVNRemote +{ + if (!self.hasSVNRepoConfig) { + self.hasSVNRepoConfig = @([self readHasSVNRemoteFromConfig]); + } + return [self.hasSVNRepoConfig boolValue]; +} + +- (NSURL *)gitURL { + return self.gtRepo.gitDirectoryURL; +} + +- (NSURL *)workingDirectoryURL { + return self.gtRepo.fileURL; +} + +- (NSString *)workingDirectory +{ + return self.workingDirectoryURL.path; +} + +- (void)forceUpdateRevisions +{ + [revisionList forceUpdate]; +} + +- (NSString *)projectName +{ + NSString* result = [self.workingDirectory lastPathComponent]; + return result; +} + +// Get the .gitignore file at the root of the repository +- (NSString *)gitIgnoreFilename +{ + return [[self workingDirectory] stringByAppendingPathComponent:@".gitignore"]; +} + +- (void)addRef:(GTReference *)gtRef +{ + GTObject *refTarget = gtRef.resolvedTarget; + if (![refTarget isKindOfClass:[GTObject class]]) { + NSLog(@"Tried to add invalid ref %@ -> %@", gtRef, refTarget); + return; + } + + GTOID *sha = refTarget.OID; + if (!sha) { + NSLog(@"Couldn't determine sha for ref %@ -> %@", gtRef, refTarget); + return; + } + + PBGitRef* ref = [[PBGitRef alloc] initWithString:gtRef.name]; +// NSLog(@"addRef %@ %@ at %@", ref.type, gtRef.name, [sha string]); + NSMutableArray* curRefs = refs[sha]; + if ( curRefs != nil ) { + if ([curRefs containsObject:ref]) { + NSLog(@"Duplicate ref shouldn't be added: %@", ref); + return; + } + [curRefs addObject:ref]; + } else { + refs[sha] = [NSMutableArray arrayWithObject:ref]; + } +} + +- (void)loadSubmodules +{ + self.submodules = [NSMutableArray array]; + + [self.gtRepo enumerateSubmodulesRecursively:NO usingBlock:^(GTSubmodule *gtSubmodule, NSError *error, BOOL *stop) { + [self.submodules addObject:gtSubmodule]; + }]; +} + +- (void) reloadRefs +{ + // clear out ref caches + _headRef = nil; + _headOID = nil; + self->refs = [NSMutableDictionary dictionary]; + + NSError* error = nil; + NSArray* allRefs = [self.gtRepo referenceNamesWithError:&error]; + + if ([self.gtRepo isHEADDetached]) { + // Add HEAD when we're detached + allRefs = [allRefs arrayByAddingObject:@"HEAD"]; + } + + // load all named refs + NSMutableOrderedSet *oldBranches = [self.branchesSet mutableCopy]; + for (NSString* referenceName in allRefs) + { + GTReference* gtRef = [self.gtRepo lookUpReferenceWithName:referenceName error:&error]; + + if (gtRef == nil) + { + NSLog(@"Reference \"%@\" could not be found in the repository", referenceName); + if (error) + { + NSLog(@"Error loading reference was: %@", error); + } + continue; + } + if (gtRef.remote && gtRef.referenceType == GTReferenceTypeSymbolic) { + // Hide remote symbolic references like origin/HEAD + continue; + } + PBGitRef* gitRef = [PBGitRef refFromString:referenceName]; + PBGitRevSpecifier* revSpec = [[PBGitRevSpecifier alloc] initWithRef:gitRef]; + [self addBranch:revSpec]; + [self addRef:gtRef]; + [oldBranches removeObject:revSpec]; + } + + for (PBGitRevSpecifier *branch in oldBranches) + if ([branch isSimpleRef] && ![branch isEqual:[self headRef]]) + [self removeBranch:branch]; + + + [self loadSubmodules]; + + [self willChangeValueForKey:@"refs"]; + [self willChangeValueForKey:@"stashes"]; + [self didChangeValueForKey:@"refs"]; + [self didChangeValueForKey:@"stashes"]; +} + +- (void) lazyReload +{ + if (!hasChanged) + return; + + [self.revisionList updateHistory]; + hasChanged = NO; +} + +- (PBGitRevSpecifier *)headRef +{ + if (_headRef && _headOID) + return _headRef; + + NSError *error = nil; + GTReference *headRef = [self.gtRepo lookUpReferenceWithName:@"HEAD" error:&error]; + if (!headRef) { + PBLogError(error); + return nil; + } + + GTReference *branchRef = [headRef resolvedReferenceWithError:&error]; + if (!branchRef && !self.gtRepo.isHEADUnborn) { + PBLogError(error); + return nil; + } else if (self.gtRepo.isHEADUnborn) { + branchRef = headRef; + } + + _headRef = [[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:branchRef.name]]; + _headOID = branchRef.OID; + + return _headRef; +} + +- (GTOID *)headOID +{ + if (! _headOID) + [self headRef]; + + return _headOID; +} + +- (PBGitCommit *)headCommit +{ + return [self commitForOID:self.headOID]; +} + +- (GTOID *)OIDForRef:(PBGitRef *)ref +{ + if (!ref) + return nil; + + for (GTOID *sha in refs) + { + NSMutableSet *refsForSha = [refs objectForKey:sha]; + for (PBGitRef *existingRef in refsForSha) + { + if ([existingRef isEqualToRef:ref]) + { + return sha; + } + } + } + + + NSError* error = nil; + GTReference *gtRef = [self.gtRepo lookUpReferenceWithName:ref.ref error:&error]; + if (!gtRef) + { + NSLog(@"Error looking up ref for %@", ref.ref); + return nil; + } + return gtRef.OID; +} + +- (PBGitCommit *)commitForRef:(PBGitRef *)ref +{ + if (!ref) + return nil; + + return [self commitForOID:[self OIDForRef:ref]]; +} + +- (PBGitCommit *)commitForOID:(GTOID *)sha +{ + if (!sha) + return nil; + NSArray *revList = revisionList.projectCommits; + + if (!revList) { + [revisionList forceUpdate]; + revList = revisionList.projectCommits; + } + for (PBGitCommit *commit in revList) + if ([commit.OID isEqual:sha]) + return commit; + + return nil; +} + +- (BOOL)isOIDOnSameBranch:(GTOID *)branchOID asOID:(GTOID *)testOID +{ + if (!branchOID || !testOID) + return NO; + + if ([testOID isEqual:branchOID]) + return YES; + + NSArray *revList = revisionList.projectCommits; + + NSMutableSet *searchOIDs = [NSMutableSet setWithObject:branchOID]; + + for (PBGitCommit *commit in revList) { + GTOID *commitOID = commit.OID; + if ([searchOIDs containsObject:commitOID]) { + if ([testOID isEqual:commitOID]) + return YES; + [searchOIDs removeObject:commitOID]; + [searchOIDs addObjectsFromArray:commit.parents]; + } + else if ([testOID isEqual:commitOID]) + return NO; + } + + return NO; +} + +- (BOOL)isOIDOnHeadBranch:(GTOID *)testOID +{ + if (!testOID) + return NO; + + GTOID *headOID = self.headOID; + + if ([testOID isEqual:headOID]) + return YES; + + return [self isOIDOnSameBranch:headOID asOID:testOID]; +} + +- (BOOL)isRefOnHeadBranch:(PBGitRef *)testRef +{ + if (!testRef) + return NO; + + return [self isOIDOnHeadBranch:[self OIDForRef:testRef]]; +} + +- (BOOL) checkRefFormat:(NSString *)refName +{ + BOOL result = [GTReference isValidReferenceName:refName]; + return result; +} + +- (BOOL) refExists:(PBGitRef *)ref +{ + NSError *gtError = nil; + GTReference *gtRef = [self.gtRepo lookUpReferenceWithName:ref.ref error:>Error]; + if (gtRef) { + return YES; + } + return NO; +} + +// useful for getting the full ref for a user entered name +// EX: name: master +// ref: refs/heads/master +- (PBGitRef *)refForName:(NSString *)name +{ + if (!name) + return nil; + + NSError *taskError = nil; + NSString *output = [self outputOfTaskWithArguments:@[@"show-ref", name] error:&taskError]; + + // the output is in the format: + // with potentially multiple lines if there are multiple matching refs (ex: refs/remotes/origin/master) + // here we only care about the first match + NSArray *refList = [output componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (refList.count != 1) return nil; + + NSString *refName = [refList objectAtIndex:1]; + return [PBGitRef refFromString:refName]; +} + +- (NSArray *)branches +{ + return [self.branchesSet array]; +} + +// Returns either this object, or an existing, equal object +- (PBGitRevSpecifier*) addBranch:(PBGitRevSpecifier*)branch +{ + if ([[branch parameters] count] == 0) + branch = [self headRef]; + + // First check if the branch doesn't exist already + if ([self.branchesSet containsObject:branch]) { + return branch; + } + + NSIndexSet *newIndex = [NSIndexSet indexSetWithIndex:[self.branches count]]; + [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:newIndex forKey:@"branches"]; + + [self.branchesSet addObject:branch]; + + [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:newIndex forKey:@"branches"]; + return branch; +} + +- (BOOL) removeBranch:(PBGitRevSpecifier *)branch +{ + if ([self.branchesSet containsObject:branch]) { + NSIndexSet *oldIndex = [NSIndexSet indexSetWithIndex:[self.branches indexOfObject:branch]]; + [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:oldIndex forKey:@"branches"]; + + [self.branchesSet removeObject:branch]; + + [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:oldIndex forKey:@"branches"]; + return YES; + } + return NO; +} + +- (void) readCurrentBranch +{ + self.currentBranch = [self addBranch: [self headRef]]; +} + +- (void) setCurrentBranch:(PBGitRevSpecifier *)newCurrentBranch { + currentBranch = newCurrentBranch; + [revisionList updateHistory]; +} + +- (void) setCurrentBranchFilter:(NSInteger)newCurrentBranchFilter { + currentBranchFilter = newCurrentBranchFilter; + [revisionList updateHistory]; +} + +- (void) setHasChanged:(BOOL)newHasChanged { + hasChanged = newHasChanged; + [revisionList forceUpdate]; +} + + +#pragma mark Stashes + +- (NSArray *)stashes +{ + NSMutableArray *stashes = [NSMutableArray array]; + [self.gtRepo enumerateStashesUsingBlock:^(NSUInteger index, NSString *message, GTOID *oid, BOOL *stop) { + PBGitStash *stash = [[PBGitStash alloc] initWithRepository:self stashOID:oid index:index message:message]; + [stashes addObject:stash]; + }]; + return [NSArray arrayWithArray:stashes]; +} + +- (PBGitStash *)stashForRef:(PBGitRef *)ref { + __block PBGitStash * found = nil; + + [self.gtRepo enumerateStashesUsingBlock:^(NSUInteger index, NSString *message, GTOID *oid, BOOL *stop) { + PBGitStash *stash = [[PBGitStash alloc] initWithRepository:self stashOID:oid index:index message:message]; + if ([stash.ref isEqualToRef:ref]) { + found = stash; + *stop = YES; + } + }]; + return found; +} + +- (BOOL)stashRunCommand:(NSString *)command withStash:(PBGitStash *)stash error:(NSError **)error +{ + NSError *gitError = nil; + NSArray *arguments = @[@"stash", command, stash.ref.refishName]; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + [self willChangeValueForKey:@"stashes"]; + [self didChangeValueForKey:@"stashes"]; + if (!output) { + NSString *title = [NSString stringWithFormat:@"Stash %@ failed!", command]; + NSString *message = [NSString stringWithFormat:@"There was an error!"]; + + return PBReturnErrorWithUserInfo(error, title, message, @{NSUnderlyingErrorKey: gitError}); + } + return YES; +} + +- (BOOL)stashPop:(PBGitStash *)stash error:(NSError **)error +{ + return [self stashRunCommand:@"pop" withStash:stash error:error]; +} + +- (BOOL)stashApply:(PBGitStash *)stash error:(NSError **)error +{ + return [self stashRunCommand:@"apply" withStash:stash error:error]; +} + +- (BOOL)stashDrop:(PBGitStash *)stash error:(NSError **)error +{ + return [self stashRunCommand:@"drop" withStash:stash error:error]; +} + +- (BOOL)stashSave:(NSError **)error +{ + return [self stashSaveWithKeepIndex:NO error:error]; +} + +- (BOOL)stashSaveWithKeepIndex:(BOOL)keepIndex error:(NSError **)error +{ + NSError *gitError = nil; + NSArray * arguments = @[@"stash", @"save", keepIndex?@"--keep-index":@"--no-keep-index"]; + + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + [self willChangeValueForKey:@"stashes"]; + [self didChangeValueForKey:@"stashes"]; + if (!output) { + NSString *title = [NSString stringWithFormat:@"Stash save failed!"]; + NSString *message = [NSString stringWithFormat:@"There was an error!"]; + + return PBReturnErrorWithUserInfo(error, title, message, @{NSUnderlyingErrorKey: gitError}); + } + return YES; +} + +- (BOOL)ignoreFilePaths:(NSArray *)filePaths error:(NSError **)error +{ + NSString *filesAsString = [filePaths componentsJoinedByString:@"\n"]; + + // Write to the file + NSString *gitIgnoreName = [self gitIgnoreFilename]; + + NSStringEncoding enc = NSUTF8StringEncoding; + NSString *ignoreFile; + + if (![[NSFileManager defaultManager] fileExistsAtPath:gitIgnoreName]) { + ignoreFile = filesAsString; + } else { + NSMutableString *currentFile = [NSMutableString stringWithContentsOfFile:gitIgnoreName usedEncoding:&enc error:error]; + if (!currentFile) return NO; + + // Add a newline if not yet present + if ([currentFile characterAtIndex:([ignoreFile length] - 1)] != '\n') + [currentFile appendString:@"\n"]; + [currentFile appendString:filesAsString]; + + ignoreFile = currentFile; + } + + return [ignoreFile writeToFile:gitIgnoreName atomically:YES encoding:enc error:error]; +} + +- (PBGitIndex *)index +{ + if (!_index) { + _index = [[PBGitIndex alloc] initWithRepository:self]; + } + return _index; +} + +#pragma mark Remotes + +- (NSArray *)remotes +{ + NSError *error = nil; + NSArray *remotes = [self.gtRepo remoteNamesWithError:&error]; + if (!remotes) { + PBLogError(error); + return nil; + } + return remotes; +} + +- (BOOL) hasRemotes +{ + return ([self remotes] != nil); +} + +- (PBGitRef *) remoteRefForBranch:(PBGitRef *)branch error:(NSError **)error +{ + if ([branch isRemote]) { + return [branch remoteRef]; + } + + NSError *gtError = nil; + BOOL success = NO; + NSAssert(branch.ref != nil, @"Unexpected nil ref"); + + GTBranch *gtBranch = [self.gtRepo lookUpBranchWithName:branch.branchName type:GTBranchTypeLocal success:&success error:>Error]; + if (!success) { + NSString *failure = [NSString stringWithFormat:NSLocalizedString(@"There was an error looking up the branch \"%@\"", @""), branch.shortName]; + PBReturnError(error, NSLocalizedString(@"Branch lookup failed", @""), failure, gtError); + return nil; + } + if (!gtBranch) { + NSString *failure = [NSString stringWithFormat:NSLocalizedString(@"There doesn't seem to be a branch named \"%@\"", @""), branch.shortName]; + PBReturnError(error, NSLocalizedString(@"Branch lookup failed", @""), failure, gtError); + return nil; + } + + GTBranch *trackingBranch = [gtBranch trackingBranchWithError:>Error success:&success]; + if (!success) { + NSString *failure = [NSString stringWithFormat:NSLocalizedString(@"There was an error finding the tracking branch of branch \"%@\"", @""), branch.shortName]; + PBReturnError(error, NSLocalizedString(@"Branch lookup failed", @""), failure, gtError); + return nil; + } + if (!trackingBranch) { + PBReturnErrorWithBuilder(error, ^{ + NSString *info = [NSString stringWithFormat:@"There is no remote configured for branch \"%@\".", branch.shortName]; + NSString *recovery = NSLocalizedString(@"Please select a branch from the popup menu, which has a corresponding remote tracking branch set up.\n\nYou can also use a contextual menu to choose a branch by right clicking on its label in the commit history list.", @""); + + return [NSError pb_errorWithDescription:NSLocalizedString(@"No remote configured for branch", @"") + failureReason:info + underlyingError:gtError + userInfo:@{NSLocalizedRecoverySuggestionErrorKey: recovery}]; + }); + return nil; + } + + NSString *trackingBranchRefName = trackingBranch.reference.name; + PBGitRef *trackingBranchRef = [PBGitRef refFromString:trackingBranchRefName]; + return trackingBranchRef; +} + +#pragma mark Repository commands + +- (BOOL)addRemote:(NSString *)remoteName withURL:(NSString *)URLString error:(NSError **)error +{ + PBTask *task = [self taskWithArguments:@[@"remote", @"add", @"-f", remoteName, URLString]]; + return [task launchTask:error]; +} + +- (BOOL)fetchRemoteForRef:(PBGitRef *)ref error:(NSError **)error +{ + NSString *fetchArg = nil; + if (ref == nil) { + fetchArg = @"--all"; + } else { + if (!ref.isRemote) { + ref = [self remoteRefForBranch:ref error:error]; + if (!ref) return NO; + } + fetchArg = ref.remoteName; + } + + PBTask *task = [self taskWithArguments:@[@"fetch", fetchArg]]; + NSError *taskError = nil; + BOOL success = [task launchTask:&taskError]; + if (!success) { + NSString *desc = NSLocalizedString(@"Fetch failed", @"PBGitRepository - fetch error description"); + NSString *reason = [NSString stringWithFormat:NSLocalizedString(@"An error occurred while fetching remote \"%@\".", @"PBGitRepostory - fetch error reason"), ref.remoteName]; + PBReturnError(error, desc, reason, taskError); + } + + + dispatch_async(dispatch_get_main_queue(), ^{ + [self reloadRefs]; + }); + + return success; +} + +- (BOOL)pullBranch:(PBGitRef *)branchRef fromRemote:(PBGitRef *)remoteRef rebase:(BOOL)rebase error:(NSError **)error +{ + NSMutableArray *arguments = [NSMutableArray arrayWithObject:@"pull"]; + + if (rebase) { + [arguments addObject:@"--rebase"]; + } + + // a nil remoteRef means lookup the ref's default remote + if (!remoteRef || ![remoteRef isRemote]) { + NSError *error = nil; + remoteRef = [self remoteRefForBranch:branchRef error:&error]; + if (!remoteRef) return NO; + } + NSString *remoteName = [remoteRef remoteName]; + [arguments addObject:remoteName]; + + PBTask *task = [self taskWithArguments:arguments]; + NSError *taskError = nil; + BOOL success = [task launchTask:&taskError]; + if (!success) { + NSString *desc = NSLocalizedString(@"Pull failed", @"PBGitRepository - pull error description"); + NSString *reason = [NSString stringWithFormat:NSLocalizedString(@"An error occurred while pulling remote \"%@\" to \"%@\".", @"PBGitRepostory - pull error reason"), remoteName, branchRef.shortName]; + PBReturnError(error, desc, reason, taskError); + } + + + dispatch_async(dispatch_get_main_queue(), ^{ + [self reloadRefs]; + }); + + return success; +} + +- (BOOL)pushBranch:(PBGitRef *)branchRef toRemote:(PBGitRef *)remoteRef error:(NSError **)error +{ + NSMutableArray *arguments = [NSMutableArray arrayWithObject:@"push"]; + + // a nil remoteRef means lookup the ref's default remote + if (!remoteRef || ![remoteRef isRemote]) { + NSError *error = nil; + remoteRef = [self remoteRefForBranch:branchRef error:&error]; + if (!remoteRef) return NO; + } + + NSString *remoteName = [remoteRef remoteName]; + [arguments addObject:remoteName]; + + NSString *branchName = nil; + if (!branchRef || branchRef.isRemote) { + branchName = @"all updates"; + } else if (branchRef.isTag) { + branchName = [NSString stringWithFormat:@"tag '%@'", [branchRef tagName]]; + [arguments addObject:@"tag"]; + [arguments addObject:[branchRef tagName]]; + } else { + branchName = [branchRef shortName]; + [arguments addObject:branchName]; + } + + PBTask *task = [self taskWithArguments:arguments]; + + NSError *taskError = nil; + BOOL success = [task launchTask:&taskError]; + if (!success) { + NSString *desc = NSLocalizedString(@"Push failed", @"PBGitRepository - push error description"); + NSString *reason = [NSString stringWithFormat:NSLocalizedString(@"An error occurred while pushing %@ to \"%@\".", @"PBGitRepostory - push error reason"), branchName, remoteName]; + PBReturnError(error, desc, reason, taskError); + } + dispatch_async(dispatch_get_main_queue(), ^{ + [self reloadRefs]; + }); + + return success; +} + +- (BOOL) checkoutRefish:(id )ref error:(NSError **)error +{ + NSString *refName = nil; + if ([ref refishType] == kGitXBranchType) + refName = [ref shortName]; + else + refName = [ref refishName]; + + NSError *gitError = nil; + NSArray *arguments = @[@"checkout", refName]; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *title = @"Checkout failed"; + NSString *message = [NSString stringWithFormat:@"There was an error checking out the %@ '%@'.\n\nPerhaps your working directory is not clean?", [ref refishType], [ref shortName]]; + + return PBReturnError(error, title, message, gitError); + } + + [self reloadRefs]; + [self readCurrentBranch]; + return YES; +} + +- (BOOL) checkoutFiles:(NSArray *)files fromRefish:(id )ref error:(NSError **)error +{ + if (!files || ([files count] == 0)) + return NO; + + NSString *refName = nil; + if ([ref refishType] == kGitXBranchType) + refName = [ref shortName]; + else + refName = [ref refishName]; + + NSArray *arguments = @[@"checkout", refName, @"--"]; + arguments = [arguments arrayByAddingObjectsFromArray:files]; + + NSError *gitError = nil; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *title = @"Checkout failed"; + NSString *message = [NSString stringWithFormat:@"There was an error checking out the file(s) from the %@ '%@'.\n\nPerhaps your working directory is not clean?", [ref refishType], [ref shortName]]; + + return PBReturnError(error, title, message, gitError); + } + + return YES; +} + + +- (BOOL) mergeWithRefish:(id )ref error:(NSError **)error +{ + NSString *refName = [ref refishName]; + + NSError *gitError = nil; + NSArray *arguments = @[@"merge", refName]; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *title = @"Merge failed!"; + NSString *headName = [[[self headRef] ref] shortName]; + NSString *message = [NSString stringWithFormat:@"There was an error merging %@ into %@.", refName, headName]; + + return PBReturnError(error, title, message, gitError); + } + + [self reloadRefs]; + [self readCurrentBranch]; + return YES; +} + +- (BOOL) cherryPickRefish:(id )ref error:(NSError **)error +{ + if (!ref) + return NO; + + NSString *refName = [ref refishName]; + + NSError *gitError = nil; + NSArray *arguments = @[@"cherry-pick", refName]; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *title = @"Cherry pick failed!"; + NSString *message = [NSString stringWithFormat:@"There was an error cherry picking the %@ '%@'.\n\nPerhaps your working directory is not clean?", [ref refishType], [ref shortName]]; + + return PBReturnError(error, title, message, gitError); + } + + [self reloadRefs]; + [self readCurrentBranch]; + return YES; +} + +- (BOOL) rebaseBranch:(id )branch onRefish:(id )upstream error:(NSError **)error +{ + NSParameterAssert(upstream != nil); + + NSArray *arguments = @[@"rebase", upstream.refishName]; + + if (branch) + arguments = [arguments arrayByAddingObject:branch.refishName]; + + NSError *gitError = nil; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *branchName = @"HEAD"; + if (branch) + branchName = [NSString stringWithFormat:@"%@ '%@'", [branch refishType], [branch shortName]]; + NSString *title = @"Rebase failed!"; + NSString *message = [NSString stringWithFormat:@"There was an error rebasing %@ with %@ '%@'.", branchName, [upstream refishType], [upstream shortName]]; + + return PBReturnError(error, title, message, gitError); + } + + [self reloadRefs]; + [self readCurrentBranch]; + return YES; +} + +- (BOOL) createBranch:(NSString *)branchName atRefish:(id )ref error:(NSError **)error +{ + if (!branchName || !ref) + return NO; + + NSError *gitError = nil; + NSArray *arguments = @[@"branch", branchName, ref.refishName]; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *title = @"Create Branch failed!"; + NSString *message = [NSString stringWithFormat:@"There was an error creating the branch '%@' at %@ '%@'.", branchName, [ref refishType], [ref shortName]]; + + return PBReturnErrorWithUserInfo(error, title, message, @{NSUnderlyingErrorKey: gitError}); + } + + [self reloadRefs]; + return YES; +} + +- (BOOL) createTag:(NSString *)tagName message:(NSString *)message atRefish:(id )target error:(NSError **)error +{ + if (!tagName) + return NO; + + GTObject *object = [self.gtRepo lookUpObjectByRevParse:[target refishName] error:error]; + if (!object) return NO; + + BOOL success = NO; + if (message.length == 0) { + success = [self.gtRepo createLightweightTagNamed:tagName target:object error:error]; + } else { + GTTag *tag = [self.gtRepo createTagNamed:tagName target:object tagger:self.gtRepo.userSignatureForNow message:message error:error]; + success = (tag != nil); + } + if (!success) return NO; + + [self reloadRefs]; + return YES; +} + +- (BOOL) deleteRemote:(PBGitRef *)ref error:(NSError **)error +{ + if (!ref || ([ref refishType] != kGitXRemoteType)) + return NO; + + NSError *gitError = nil; + NSArray *arguments = @[@"remote", @"rm", ref.remoteName]; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *title = @"Delete remote failed!"; + NSString *message = [NSString stringWithFormat:@"There was an error deleting the remote: %@\n\n", [ref remoteName]]; + return PBReturnErrorWithUserInfo(error, title, message, @{NSUnderlyingErrorKey: gitError}); + } + + // remove the remote's branches + NSString *remoteRef = [kGitXRemoteRefPrefix stringByAppendingString:[ref remoteName]]; + for (PBGitRevSpecifier *rev in [self.branchesSet copy]) { + PBGitRef *branch = [rev ref]; + if ([[branch ref] hasPrefix:remoteRef]) { + [self removeBranch:rev]; + PBGitCommit *commit = [self commitForRef:branch]; + [commit removeRef:branch]; + } + } + + [self reloadRefs]; + return YES; +} + +- (NSString *)performDiff:(PBGitCommit *)startCommit against:(PBGitCommit *)diffCommit forFiles:(NSArray *)filePaths { + NSParameterAssert(startCommit); + NSAssert(startCommit.repository == self, @"Different repo"); + + if (diffCommit) { + NSAssert(diffCommit.repository == self, @"Different repo"); + } else { + diffCommit = [self headCommit]; + } + + NSString *commitSelector = [NSString stringWithFormat:@"%@..%@", startCommit.SHA, diffCommit.SHA]; + NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"diff", @"--no-ext-diff", commitSelector, nil]; + + if (![PBGitDefaults showWhitespaceDifferences]) + [arguments insertObject:@"-w" atIndex:1]; + + if (filePaths) { + [arguments addObject:@"--"]; + [arguments addObjectsFromArray:filePaths]; + } + + NSError *error = nil; + NSString *diff = [self outputOfTaskWithArguments:arguments error:&error]; + if (!diff) { + PBLogError(error); + return @""; + } + return diff; +} + +- (BOOL) deleteRef:(PBGitRef *)ref error:(NSError **)error +{ + if (!ref) + return NO; + + if ([ref refishType] == kGitXRemoteType) + return [self deleteRemote:ref error:error]; + + NSError *gitError = nil; + NSArray *arguments = @[@"update-ref", @"-d", ref.ref]; + NSString *output = [self outputOfTaskWithArguments:arguments error:&gitError]; + if (!output) { + NSString *title = @"Delete ref failed!"; + NSString *message = [NSString stringWithFormat:@"There was an error deleting the ref: %@\n\n", [ref shortName]]; + + return PBReturnErrorWithUserInfo(error, title, message, @{NSUnderlyingErrorKey: gitError}); + } + + [self removeBranch:[[PBGitRevSpecifier alloc] initWithRef:ref]]; + PBGitCommit *commit = [self commitForRef:ref]; + [commit removeRef:ref]; + + [self reloadRefs]; + return YES; +} + +- (BOOL)updateReference:(PBGitRef *)ref toPointAtCommit:(PBGitCommit *)newCommit { + NSError *error = nil; + BOOL success = [self launchTaskWithArguments:@[@"update-ref", @"-mUpdate from GitX", ref.ref, newCommit.SHA] error:&error]; + if (!success) { + PBLogError(error); + } + return success; +} + +- (GTSubmodule *)submoduleAtPath:(NSString *)path error:(NSError **)error; +{ + NSString *standardizedPath = path.stringByStandardizingPath; + for (GTSubmodule *submodule in self.submodules) { + if ([standardizedPath hasSuffix:submodule.path]) { + return submodule; + } + } + if (error) { + NSString *failure = [NSString stringWithFormat:@"The submodule at path \"%@\" couldn't be found.", path]; + *error = [NSError pb_errorWithDescription:@"Submodule not found" failureReason:failure]; + } + return nil; +} + +#pragma mark Hooks + +- (BOOL)executeHook:(NSString *)name error:(NSError **)error { + return [self executeHook:name arguments:@[] error:error]; +} + +- (BOOL)executeHook:(NSString *)name arguments:(NSArray *)arguments error:(NSError **)error { + return [self executeHook:name arguments:arguments output:NULL error:error]; +} + +- (BOOL)executeHook:(NSString *)name arguments:(NSArray *)arguments output:(NSString **)outputPtr error:(NSError **)error { + NSParameterAssert(name != nil); + + NSString *hookPath = [[[[self gitURL] path] stringByAppendingPathComponent:@"hooks"] stringByAppendingPathComponent:name]; + if (![[NSFileManager defaultManager] isExecutableFileAtPath:hookPath]) { + // XXX: Maybe return error ? + return YES; + } + + PBTask *task = [PBTask taskWithLaunchPath:hookPath arguments:arguments inDirectory:self.workingDirectory]; + task.additionalEnvironment = @{ + @"GIT_DIR": self.gitURL.path, + @"GIT_INDEX_FILE": [self.gitURL.path stringByAppendingPathComponent:@"index"], + }; + + NSError *taskError = nil; + BOOL success = [task launchTask:&taskError]; + + NSString *output = task.standardOutputString; + if (!success) { + return PBReturnErrorWithBuilder(error, ^{ + NSString *failureReason = [NSString localizedStringWithFormat:@"Hook %@ failed", name]; + NSString *desc = nil; + if (output.length == 0) { + desc = [NSString localizedStringWithFormat:@"The %@ hook failed to run.", name]; + } else { + desc = [NSString localizedStringWithFormat:@"The %@ hook failed to run and returned the following:\n%@", name, output]; + } + return [NSError pb_errorWithDescription:desc failureReason:failureReason underlyingError:taskError]; + }); + } + + if (outputPtr) *outputPtr = output; + + return YES; +} + +- (BOOL)revisionExists:(NSString *)spec +{ + return [self.gtRepo lookUpObjectByRevParse:spec error:nil] != nil; +} + +@end diff --git a/Classes/git/PBGitRepositoryWatcher.h b/Classes/git/PBGitRepositoryWatcher.h new file mode 100644 index 000000000..ffba34964 --- /dev/null +++ b/Classes/git/PBGitRepositoryWatcher.h @@ -0,0 +1,38 @@ +// +// PBGitHistoryWatcher.h +// GitX +// +// Watches a specified path +// +// Created by Dave Grijalva on 1/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class PBGitRepository; + +typedef NS_ENUM(NSUInteger, PBGitRepositoryWatcherEventType) { + PBGitRepositoryWatcherEventTypeNone = (1 << 0), + PBGitRepositoryWatcherEventTypeGitDirectory = (1 << 1), + PBGitRepositoryWatcherEventTypeWorkingDirectory = (1 << 2), + PBGitRepositoryWatcherEventTypeIndex = (1 << 3), +}; + +extern NSString *PBGitRepositoryEventNotification; +extern NSString *kPBGitRepositoryEventTypeUserInfoKey; +extern NSString *kPBGitRepositoryEventPathsUserInfoKey; + +@interface PBGitRepositoryWatcher : NSObject + +@property (readonly, weak) PBGitRepository *repository; + +- (instancetype) initWithRepository:(PBGitRepository *)repository; +- (void) start; +- (void) stop; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/git/PBGitRepositoryWatcher.m b/Classes/git/PBGitRepositoryWatcher.m new file mode 100644 index 000000000..34e6b8513 --- /dev/null +++ b/Classes/git/PBGitRepositoryWatcher.m @@ -0,0 +1,305 @@ +// +// PBGitRepositoryWatcher.m +// GitX +// +// Created by Dave Grijalva on 1/26/09. +// Copyright 2009 __MyCompanyName__. All rights reserved. +// +#import + +#import "PBGitRepositoryWatcher.h" +#import "PBGitRepository.h" +#import "PBGitDefaults.h" + +NSString *PBGitRepositoryEventNotification = @"PBGitRepositoryModifiedNotification"; +NSString *kPBGitRepositoryEventTypeUserInfoKey = @"kPBGitRepositoryEventTypeUserInfoKey"; +NSString *kPBGitRepositoryEventPathsUserInfoKey = @"kPBGitRepositoryEventPathsUserInfoKey"; + +typedef void (^PBGitRepositoryWatcherCallbackBlock)(NSArray *changedFiles); + +/* Small helper class to keep track of events */ +@interface PBGitRepositoryWatcherEventPath : NSObject +@property NSString *path; +@property (assign) FSEventStreamEventFlags flag; +@end + +@implementation PBGitRepositoryWatcherEventPath +@end + +@interface PBGitRepositoryWatcher () { + FSEventStreamRef eventStream; + NSDate *gitDirTouchDate; + NSDate *indexTouchDate; + + BOOL _running; +} + +@property (readonly) NSString *gitDir; +@property (readonly) NSString *workDir; + +@property (nonatomic, strong) NSMutableDictionary *statusCache; + +- (void) handleGitDirEventCallback:(NSArray *)eventPaths; +- (void) handleWorkDirEventCallback:(NSArray *)eventPaths; + +@end + +void PBGitRepositoryWatcherCallback(ConstFSEventStreamRef streamRef, + void *clientCallBackInfo, + size_t numEvents, + void *_eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]){ + PBGitRepositoryWatcher *watcher = (__bridge PBGitRepositoryWatcher *)clientCallBackInfo; + + NSMutableArray *gitDirEvents = [NSMutableArray array]; + NSMutableArray *workDirEvents = [NSMutableArray array]; + NSArray *eventPaths = (__bridge NSArray*)_eventPaths; + for (int i = 0; i < numEvents; ++i) { + NSString *path = [eventPaths objectAtIndex:i]; + PBGitRepositoryWatcherEventPath *ep = [[PBGitRepositoryWatcherEventPath alloc] init]; + ep.path = [path stringByStandardizingPath]; + ep.flag = eventFlags[i]; + + + if ([ep.path hasPrefix:watcher.gitDir]) { + // exclude all changes to .lock files + if ([ep.path hasSuffix:@".lock"]) { + continue; + } + [gitDirEvents addObject:ep]; + } else if ([ep.path hasPrefix:watcher.workDir]) { + [workDirEvents addObject:ep]; + } + } + + if (workDirEvents.count) { + [watcher handleWorkDirEventCallback:workDirEvents]; + } + if (gitDirEvents.count) { + [watcher handleGitDirEventCallback:gitDirEvents]; + } +} + +@implementation PBGitRepositoryWatcher + +- (instancetype) initWithRepository:(PBGitRepository *)theRepository { + NSParameterAssert(theRepository != nil); + + self = [super init]; + if (!self) { + return nil; + } + + _repository = theRepository; + _statusCache = [NSMutableDictionary new]; + + if ([PBGitDefaults useRepositoryWatcher]) + [self start]; + return self; +} + +- (void)dealloc { + if (eventStream) { + FSEventStreamStop(eventStream); + FSEventStreamInvalidate(eventStream); + FSEventStreamRelease(eventStream); + } +} + +- (NSDate *) fileModificationDateAtPath:(NSString *)path { + NSError* error; + NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:path + error:&error]; + if (error) + { + NSLog(@"Unable to get attributes of \"%@\"", path); + return nil; + } + return [attrs objectForKey:NSFileModificationDate]; +} + +- (BOOL) indexChanged { + if (self.repository.isBareRepository) { + return NO; + } + + NSDate *newTouchDate = [self fileModificationDateAtPath:[self.gitDir stringByAppendingPathComponent:@"index"]]; + if (![newTouchDate isEqual:indexTouchDate]) { + indexTouchDate = newTouchDate; + return YES; + } + + return NO; +} + +- (BOOL) gitDirectoryChanged { + + NSArray *properties = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey]; + NSArray *urls = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:self.repository.gitURL + includingPropertiesForKeys:properties + options:0 + error:nil]; + for (NSURL *fileURL in urls) + { + NSNumber *number = nil; + if (![fileURL getResourceValue:&number forKey:NSURLIsDirectoryKey error:nil] || [number boolValue]) { + continue; + } + + NSDate *modTime = nil; + if (![fileURL getResourceValue:&modTime forKey:NSURLContentModificationDateKey error:nil]) + continue; + + if (gitDirTouchDate == nil || [modTime compare:gitDirTouchDate] == NSOrderedDescending) + { + NSDate* newModTime = [modTime laterDate:gitDirTouchDate]; + + gitDirTouchDate = newModTime; + return YES; + } + } + return NO; +} + +- (void) handleGitDirEventCallback:(NSArray *)eventPaths +{ + PBGitRepositoryWatcherEventType event = 0x0; + + if ([self indexChanged]) { + event |= PBGitRepositoryWatcherEventTypeIndex; + } + + + NSMutableArray *paths = [NSMutableArray array]; + for (PBGitRepositoryWatcherEventPath *eventPath in eventPaths) { + // .git dir + if ([eventPath.path isEqualToString:self.gitDir]) { + if ([self gitDirectoryChanged] || eventPath.flag != kFSEventStreamEventFlagNone) { + event |= PBGitRepositoryWatcherEventTypeGitDirectory; + [paths addObject:eventPath.path]; + } + } + // ignore objects dir ... ? + else if ([eventPath.path rangeOfString:[self.gitDir stringByAppendingPathComponent:@"objects"]].location != NSNotFound) { + continue; + } + // index is already covered + else if ([eventPath.path rangeOfString:[self.gitDir stringByAppendingPathComponent:@"index"]].location != NSNotFound) { + continue; + } + // subdirs of .git dir + else if ([eventPath.path rangeOfString:self.gitDir].location != NSNotFound) { + event |= PBGitRepositoryWatcherEventTypeGitDirectory; + [paths addObject:eventPath.path]; + } + } + + if(event != 0x0){ + NSDictionary *eventInfo = @{kPBGitRepositoryEventTypeUserInfoKey:@(event), + kPBGitRepositoryEventPathsUserInfoKey:paths}; + + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitRepositoryEventNotification object:self.repository userInfo:eventInfo]; + } +} + +- (void)handleWorkDirEventCallback:(NSArray *)eventPaths +{ + PBGitRepositoryWatcherEventType event = 0x0; + + NSMutableArray *paths = [NSMutableArray array]; + for (PBGitRepositoryWatcherEventPath *eventPath in eventPaths) { + unsigned int fileStatus = 0; + if (![eventPath.path hasPrefix:self.workDir]) { + continue; + } + if ([eventPath.path isEqualToString:self.workDir]) { + event |= PBGitRepositoryWatcherEventTypeWorkingDirectory; + [paths addObject:eventPath.path]; + continue; + } + NSString *eventRepoRelativePath = [eventPath.path substringFromIndex:(self.workDir.length + 1)]; + int ignoreResult = 0; + int ignoreError = git_status_should_ignore(&ignoreResult, self.repository.gtRepo.git_repository, eventRepoRelativePath.UTF8String); + if (ignoreError == GIT_OK && ignoreResult) { + // file is covered by ignore rules + NSNumber *oldStatus = self.statusCache[eventPath.path]; + if (!oldStatus || [oldStatus isEqualToNumber:@(GIT_STATUS_IGNORED)]) { + // no cached status or previously ignored - skip this file + continue; + } + } + int statusError = git_status_file(&fileStatus, self.repository.gtRepo.git_repository, eventRepoRelativePath.UTF8String); + if (statusError == GIT_OK) { + NSNumber *newStatus = @(fileStatus); + self.statusCache[eventPath.path] = newStatus; + + [paths addObject:eventPath.path]; + event |= PBGitRepositoryWatcherEventTypeWorkingDirectory; + } + } + + if(event != 0x0){ + NSDictionary *eventInfo = @{kPBGitRepositoryEventTypeUserInfoKey:@(event), + kPBGitRepositoryEventPathsUserInfoKey:paths}; + + [[NSNotificationCenter defaultCenter] postNotificationName:PBGitRepositoryEventNotification object:self.repository userInfo:eventInfo]; + } +} + +- (NSString *)gitDir { + return [self.repository.gtRepo.gitDirectoryURL.path stringByStandardizingPath]; +} + +- (NSString *)workDir { + return !self.repository.gtRepo.isBare ? [self.repository.gtRepo.fileURL.path stringByStandardizingPath] : nil; +} + +- (void) _initializeStream { + if (eventStream) return; + + NSMutableArray *array = [NSMutableArray array]; + if (self.gitDir) [array addObject:self.gitDir]; + if (self.workDir) [array addObject:self.workDir]; + + if (!array.count) return; + + FSEventStreamContext gitDirWatcherContext = {0, (__bridge void *)(self), NULL, NULL, NULL}; + eventStream = FSEventStreamCreate(kCFAllocatorDefault, PBGitRepositoryWatcherCallback, &gitDirWatcherContext, + (__bridge CFArrayRef)array, + kFSEventStreamEventIdSinceNow, 1.0, + kFSEventStreamCreateFlagUseCFTypes | + kFSEventStreamCreateFlagIgnoreSelf | + kFSEventStreamCreateFlagFileEvents); +} + +- (void) start { + if (_running) + return; + + // set initial state + [self gitDirectoryChanged]; + [self indexChanged]; + [self _initializeStream]; + + if (!eventStream) return; + + FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + FSEventStreamStart(eventStream); + + _running = YES; +} + +- (void) stop { + if (!_running) + return; + + if (eventStream) { + FSEventStreamStop(eventStream); + FSEventStreamUnscheduleFromRunLoop(eventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } + + _running = NO; +} + +@end diff --git a/Classes/git/PBGitRepository_PBGitBinarySupport.h b/Classes/git/PBGitRepository_PBGitBinarySupport.h new file mode 100644 index 000000000..fb322981f --- /dev/null +++ b/Classes/git/PBGitRepository_PBGitBinarySupport.h @@ -0,0 +1,38 @@ +// +// PBGitRepository_PBGitBinarySupport.h +// GitX +// +// Created by Etienne on 22/02/2017. +// +// + +#import +#import "PBGitRepository.h" +#import "PBTask.h" // Imported so our includers don't have to add it + +NS_ASSUME_NONNULL_BEGIN + +@interface PBGitRepository (PBGitBinarySupport) +- (PBTask *)taskWithArguments:(nullable NSArray *)arguments; +- (BOOL)launchTaskWithArguments:(nullable NSArray *)arguments error:(NSError **)error; +- (nullable NSString *)outputOfTaskWithArguments:(nullable NSArray *)arguments error:(NSError **)error; +@end + +#undef GITX_DEPRECATED +#define GITX_DEPRECATED + +@interface PBGitRepository (PBGitBinarySupportDeprecated) +- (NSFileHandle*) handleForArguments:(NSArray*) args GITX_DEPRECATED; +- (NSString*) outputForCommand:(NSString*) cmd GITX_DEPRECATED; +- (NSString *)outputForCommand:(NSString *)str retValue:(int *)ret GITX_DEPRECATED; +- (NSString *)outputForArguments:(NSArray *)arguments inputString:(NSString *)input retValue:(int *)ret GITX_DEPRECATED; +- (NSString *)outputForArguments:(NSArray *)arguments inputString:(NSString *)input byExtendingEnvironment:(NSDictionary *)dict retValue:(int *)ret GITX_DEPRECATED; + + +- (NSString*) outputForArguments:(NSArray*) args GITX_DEPRECATED; +- (NSString*) outputForArguments:(NSArray*) args retValue:(int *)ret GITX_DEPRECATED; +- (NSString *)outputInWorkdirForArguments:(NSArray*) arguments GITX_DEPRECATED; +- (NSString *)outputInWorkdirForArguments:(NSArray*) arguments retValue:(int *)ret GITX_DEPRECATED; +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/git/PBGitRepository_PBGitBinarySupport.m b/Classes/git/PBGitRepository_PBGitBinarySupport.m new file mode 100644 index 000000000..9df603fda --- /dev/null +++ b/Classes/git/PBGitRepository_PBGitBinarySupport.m @@ -0,0 +1,102 @@ +// +// PBGitRepository_PBGitBinarySupport.m +// GitX +// +// Created by Etienne on 22/02/2017. +// +// + +#import "PBGitRepository_PBGitBinarySupport.h" + +#import "PBEasyPipe.h" +#import "PBGitBinary.h" +#import "PBTask.h" + +@implementation PBGitRepository (PBGitBinarySupport) + +- (PBTask *)taskWithArguments:(NSArray *)arguments +{ + NSArray *realArgs = @[[@"--git-dir=" stringByAppendingString:self.gitURL.path]]; + + // Prepend a --git-dir argument in case we're running against a bare repository + realArgs = [realArgs arrayByAddingObjectsFromArray:arguments]; + + return [PBTask taskWithLaunchPath:[PBGitBinary path] arguments:realArgs inDirectory:self.workingDirectory]; +} + +- (BOOL)launchTaskWithArguments:(nullable NSArray *)arguments error:(NSError **)error { + PBTask *task = [self taskWithArguments:arguments]; + return [task launchTask:error]; +} + +- (NSString *)outputOfTaskWithArguments:(NSArray *)arguments error:(NSError **)error { + PBTask *task = [self taskWithArguments:arguments]; + BOOL success = [task launchTask:error]; + if (!success) return nil; + + return [task standardOutputString]; +} + +@end + +@implementation PBGitRepository (PBGitBinarySupportDeprecated) + +- (NSFileHandle*) handleForArguments:(NSArray *)args +{ + NSString* gitDirArg = [@"--git-dir=" stringByAppendingString:self.gitURL.path]; + NSMutableArray* arguments = [NSMutableArray arrayWithObject: gitDirArg]; + [arguments addObjectsFromArray: args]; + return [PBEasyPipe handleForCommand:[PBGitBinary path] withArgs: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:[PBGitBinary path] withArgs:arguments inDir: self.workingDirectory]; +} + +- (NSString*) outputInWorkdirForArguments:(NSArray*) arguments +{ + return [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:self.workingDirectory]; +} + +- (NSString*) outputInWorkdirForArguments:(NSArray *)arguments retValue:(int *)ret +{ + return [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:self.workingDirectory retValue: ret]; +} + +- (NSString*) outputForArguments:(NSArray *)arguments retValue:(int *)ret +{ + return [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir: self.workingDirectory retValue: ret]; +} + +- (NSString*) outputForArguments:(NSArray *)arguments inputString:(NSString *)input retValue:(int *)ret +{ + return [PBEasyPipe outputForCommand:[PBGitBinary path] + withArgs:arguments + inDir:self.workingDirectory + inputString:input + retValue: ret]; +} + +- (NSString *)outputForArguments:(NSArray *)arguments inputString:(NSString *)input byExtendingEnvironment:(NSDictionary *)dict retValue:(int *)ret +{ + return [PBEasyPipe outputForCommand:[PBGitBinary path] + withArgs:arguments + inDir:self.workingDirectory + byExtendingEnvironment:dict + inputString:input + retValue: ret]; +} +@end diff --git a/Classes/git/PBGitRevList.h b/Classes/git/PBGitRevList.h new file mode 100644 index 000000000..f5243657a --- /dev/null +++ b/Classes/git/PBGitRevList.h @@ -0,0 +1,24 @@ +// +// PBGitRevList.h +// GitX +// +// Created by Pieter de Bie on 17-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + +@class PBGitRepository; +@class PBGitRevSpecifier; +@class PBGitCommit; + +@interface PBGitRevList : NSObject + +@property (nonatomic, assign, getter=isParsing, readonly) BOOL parsing; +@property (nonatomic, strong) NSMutableArray *commits; + +- (id) initWithRepository:(PBGitRepository *)repo rev:(PBGitRevSpecifier *)rev shouldGraph:(BOOL)graph; +- (void)loadRevisonsWithCompletionBlock:(void(^)(void))completionBlock; +- (void)cancel; + +@end diff --git a/Classes/git/PBGitRevList.mm b/Classes/git/PBGitRevList.mm new file mode 100644 index 000000000..03e84b3f4 --- /dev/null +++ b/Classes/git/PBGitRevList.mm @@ -0,0 +1,275 @@ +// +// PBGitRevList.m +// GitX +// +// Created by Pieter de Bie on 17-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitRevList.h" +#import "PBGitRepository.h" +#import "PBGitCommit.h" +#import "PBGitGrapher.h" +#import "PBGitRevSpecifier.h" +#import "PBGitBinary.h" +#import "PBError.h" + +#import +#import "ObjectiveGit+PBCategories.h" + +#import +#import +#import +#import + +using namespace std; + + +@interface PBGitRevList () + +@property (nonatomic, assign) BOOL isGraphing; +@property (nonatomic, assign) BOOL resetCommits; + +@property (nonatomic, weak) PBGitRepository *repository; +@property (nonatomic, strong) PBGitRevSpecifier *currentRev; + +@property (nonatomic, strong) NSCache *commitCache; + +@property (nonatomic, strong) NSOperationQueue *operationQueue; + +@end + + +#define kRevListRevisionsKey @"revisions" + + +@implementation PBGitRevList + +- (id) initWithRepository:(PBGitRepository *)repo rev:(PBGitRevSpecifier *)rev shouldGraph:(BOOL)graph +{ + self = [super init]; + if (!self) { + return nil; + } + self.repository = repo; + self.currentRev = [rev copy]; + self.isGraphing = graph; + self.commitCache = [[NSCache alloc] init]; + self.operationQueue = [[NSOperationQueue alloc] init]; + self.operationQueue.maxConcurrentOperationCount = 1; + self.operationQueue.qualityOfService = NSQualityOfServiceUtility; + + return self; +} + +- (void)loadRevisonsWithCompletionBlock:(void(^)(void))completionBlock +{ + [self cancel]; + + self.resetCommits = YES; + + NSBlockOperation *parseOperation = [[NSBlockOperation alloc] init]; + + __weak typeof(self) weakSelf = self; + __weak typeof(parseOperation) weakParseOperation = parseOperation; + + [parseOperation addExecutionBlock:^{ + PBGitRepository *pbRepo = weakSelf.repository; + GTRepository *repo = pbRepo.gtRepo; + + NSError *error = nil; + GTEnumerator *enu = [[GTEnumerator alloc] initWithRepository:repo error:&error]; + + [weakSelf setupEnumerator:enu forRevspec:weakSelf.currentRev]; + [weakSelf addCommitsFromEnumerator:enu inPBRepo:pbRepo operation:weakParseOperation]; + }]; + [parseOperation setCompletionBlock:completionBlock]; + + [self.operationQueue addOperation:parseOperation]; +} + + +- (void)cancel +{ + [self.operationQueue cancelAllOperations]; +} + +- (BOOL)isParsing +{ + return self.operationQueue.operationCount > 0; +} + + +- (void) updateCommits:(NSArray *)revisions operation:(NSOperation *)operation +{ + if (!revisions || [revisions count] == 0 || operation.cancelled) + return; + + if (self.resetCommits) { + self.commits = [NSMutableArray array]; + self.resetCommits = NO; + } + + NSRange range = NSMakeRange([self.commits count], [revisions count]); + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:range]; + + [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"commits"]; + [self.commits addObjectsFromArray:revisions]; + [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"commits"]; +} + +static BOOL hasParameter(NSMutableArray *parameters, NSString *paramName) { + NSUInteger index = NSNotFound; + + index = [parameters indexOfObject:paramName]; + if (index == NSNotFound) return NO; + + [parameters removeObjectAtIndex:index]; + return YES; +} + +- (void) setupEnumerator:(GTEnumerator*)enumerator + forRevspec:(PBGitRevSpecifier *)rev +{ + NSError *error = nil; + BOOL success = NO; + GTRepository *repo = enumerator.repository; + [enumerator resetWithOptions:GTEnumeratorOptionsTopologicalSort|GTEnumeratorOptionsTimeSort]; + + if (rev.isSimpleRef) { + GTObject *object = [repo lookUpObjectByRevParse:rev.simpleRef error:&error]; + if (object) { + success = [enumerator pushSHA:object.SHA error:&error]; + } + if (!object || (object && !success)) { + NSLog(@"Failed to push simple ref %@: %@", rev.simpleRef, error); + } + return; + } + + NSMutableArray *parameters = [rev.parameters mutableCopy]; + BOOL addBranches = hasParameter(parameters, @"--branches"); + BOOL addRemotes = hasParameter(parameters, @"--remotes"); + BOOL addTags = hasParameter(parameters, @"--tags"); + + NSArray *allRefs = [repo referenceNamesWithError:&error]; + + // First, loop over all the known references, and add the ones we want + if (addBranches || addRemotes || addTags) { + for (NSString *referenceName in allRefs) { + if ((addBranches && [referenceName hasPrefix:[GTBranch localNamePrefix]]) + || (addRemotes && [referenceName hasPrefix:[GTBranch remoteNamePrefix]]) + || (addTags && [referenceName hasPrefix:@"refs/tags/"])) { + success = [enumerator pushReferenceName:referenceName error:&error]; + if (!success) { + NSLog(@"Failed to push reference %@: %@", referenceName, error); + } + } + } + } + + // Handle the rest of our (less obvious) parameters + for (NSString *param in parameters) { + GTObject *obj = nil; + if ([param hasPrefix:@"--glob="]) { + success = [enumerator pushGlob:[param substringFromIndex:@"--glob=".length] error:&error]; + } else if ([param isEqualToString:@"HEAD"]) { + success = [enumerator pushHEAD:&error]; + } else if ((obj = [repo lookUpObjectByRevParse:param error:&error])) { + success = [enumerator pushSHA:obj.SHA error:&error]; + } else { + int gitError = git_revwalk_push_range(enumerator.git_revwalk, param.UTF8String); + if (gitError != GIT_OK) { + NSString *desc = [NSString stringWithFormat:@"Failed to push range"]; + NSString *fail = [NSString stringWithFormat:@"The range %@ couldn't be pushed", param]; + error = [NSError errorWithDomain:GTGitErrorDomain + code:gitError + userInfo:@{ + NSLocalizedDescriptionKey: desc, + NSLocalizedFailureReasonErrorKey: fail, + }]; + success = NO; + } else { + success = YES; + } + } + + if (!success) { + NSLog(@"Failed to push remaining parameter %@: %@", param, error); + } + } + +} + +- (void) addCommitsFromEnumerator:(GTEnumerator *)enumerator inPBRepo:(PBGitRepository*)pbRepo operation:(NSOperation *)operation +{ + PBGitGrapher *g = [[PBGitGrapher alloc] initWithRepository:pbRepo]; + __block NSDate *lastUpdate = [NSDate date]; + + dispatch_queue_t loadQueue = dispatch_queue_create("com.codebasesaga.macOS.GitX.loadQueue", 0); + dispatch_queue_t decorateQueue = dispatch_queue_create("com.codebasesaga.macOS.GitX.decorateQueue", 0); + dispatch_group_t loadGroup = dispatch_group_create(); + dispatch_group_t decorateGroup = dispatch_group_create(); + + BOOL enumSuccess = FALSE; + __block int num = 0; + __block NSMutableArray *revisions = [NSMutableArray array]; + NSError *enumError = nil; + GTOID *oid = nil; + while ((oid = [enumerator nextOIDWithSuccess:&enumSuccess error:&enumError]) && enumSuccess && !operation.cancelled) { + dispatch_group_async(loadGroup, loadQueue, ^{ + if (operation.cancelled) { + return; + } + + PBGitCommit *newCommit = nil; + PBGitCommit *cachedCommit = [self.commitCache objectForKey:oid]; + if (cachedCommit) { + newCommit = cachedCommit; + } else { + GTCommit *commit = (GTCommit *)[pbRepo.gtRepo lookUpObjectByOID:oid error:NULL]; + if (!commit) { + [NSException raise:NSInternalInconsistencyException format:@"Missing commit with OID %@", oid]; + } + + newCommit = [[PBGitCommit alloc] initWithRepository:pbRepo andCommit:commit]; + [self.commitCache setObject:newCommit forKey:oid]; + } + + [revisions addObject:newCommit]; + + if (self.isGraphing) { + dispatch_group_async(decorateGroup, decorateQueue, ^{ + [g decorateCommit:newCommit]; + }); + } + + if (++num % 100 == 0 && [[NSDate date] timeIntervalSinceDate:lastUpdate] > 0.2) { + dispatch_group_wait(decorateGroup, DISPATCH_TIME_FOREVER); + + NSArray *updatedRevisions = [revisions copy]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateCommits:updatedRevisions operation:operation]; + }); + + [revisions removeAllObjects]; + lastUpdate = [NSDate date]; + } + }); + } + + NSAssert(!enumError, @"Error enumerating commits"); + + dispatch_group_wait(loadGroup, DISPATCH_TIME_FOREVER); + dispatch_group_wait(decorateGroup, DISPATCH_TIME_FOREVER); + + // Make sure the commits are stored before exiting. + NSArray *updatedRevisions = [revisions copy]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateCommits:updatedRevisions operation:operation]; + }); +} + +@end diff --git a/PBGitRevSpecifier.h b/Classes/git/PBGitRevSpecifier.h similarity index 63% rename from PBGitRevSpecifier.h rename to Classes/git/PBGitRevSpecifier.h index 0461908d5..508d9d544 100644 --- a/PBGitRevSpecifier.h +++ b/Classes/git/PBGitRevSpecifier.h @@ -7,23 +7,27 @@ // #import -#import +@class PBGitRef; -@interface PBGitRevSpecifier : NSObject { +@interface PBGitRevSpecifier : NSObject { NSString *description; NSArray *parameters; NSURL *workingDirectory; + BOOL isSimpleRef; } +- (id) initWithParameters:(NSArray *)params description:(NSString *)descrip; - (id) initWithParameters:(NSArray*) params; - (id) initWithRef: (PBGitRef*) ref; -- (BOOL) isSimpleRef; - (NSString*) simpleRef; +- (PBGitRef *) ref; - (BOOL) hasPathLimiter; -- (BOOL) hasLeftRight; +- (NSString *) title; -- (BOOL) isEqualTo: (PBGitRevSpecifier*) other; +- (BOOL) isEqual: (PBGitRevSpecifier*) other; +- (BOOL) isAllBranchesRev; +- (BOOL) isLocalBranchesRev; + (PBGitRevSpecifier *)allBranchesRevSpec; + (PBGitRevSpecifier *)localBranchesRevSpec; @@ -31,5 +35,6 @@ @property(retain) NSString *description; @property(readonly) NSArray *parameters; @property(retain) NSURL *workingDirectory; +@property(readonly) BOOL isSimpleRef; @end diff --git a/Classes/git/PBGitRevSpecifier.m b/Classes/git/PBGitRevSpecifier.m new file mode 100644 index 000000000..5826b92dd --- /dev/null +++ b/Classes/git/PBGitRevSpecifier.m @@ -0,0 +1,170 @@ +// +// PBGitRevSpecifier.m +// GitX +// +// Created by Pieter de Bie on 12-09-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitRevSpecifier.h" +#import "PBGitRef.h" + +@implementation PBGitRevSpecifier + +@synthesize parameters, description, workingDirectory; +@synthesize isSimpleRef; + +// I believe this relates loosely to parts of git-check-ref-format. +// cf. https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html +// +NS_INLINE BOOL ContainsComplexRefCharSequence(NSString *refString) +{ + return ([refString hasPrefix:@"-"] || + [refString rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" ~^:"]].location != NSNotFound || + [refString rangeOfString:@".."].location != NSNotFound || + [refString rangeOfString:@"@{"].location != NSNotFound); +} + +// internal designated init +- (id) initWithParameters:(NSArray *)params description:(NSString *)descrip +{ + NSParameterAssert(params != nil); + + self = [super init]; + if (!self) return nil; + + parameters = params; + description = descrip; + isSimpleRef = (params.count == 1) && !ContainsComplexRefCharSequence(params[0]); + + return self; +} + +- (id) initWithParameters:(NSArray *)params +{ + return [self initWithParameters:params description:nil]; +} + +- (id) initWithRef:(PBGitRef *)ref +{ + return [self initWithParameters:[NSArray arrayWithObject:ref.ref] description:ref.shortName]; +} + +- (id) initWithCoder:(NSCoder *)coder +{ + return [self initWithParameters:[coder decodeObjectForKey:@"Parameters"] description:[coder decodeObjectForKey:@"Description"]]; +} + ++ (PBGitRevSpecifier *)allBranchesRevSpec +{ + // Using --all here would include refs like refs/notes/commits, which probably isn't what we want. + return [[PBGitRevSpecifier alloc] initWithParameters:@[@"--branches", @"--remotes", @"--tags", @"--glob=refs/stash*", @"HEAD"] description:@"All branches"]; +} + ++ (PBGitRevSpecifier *)localBranchesRevSpec +{ + return [[PBGitRevSpecifier alloc] initWithParameters:@[@"--branches", @"HEAD"] description:@"Local branches"]; +} + +- (NSString*) simpleRef +{ + if (![self isSimpleRef]) + return nil; + return [parameters objectAtIndex:0]; +} + +- (PBGitRef *) ref +{ + if (![self isSimpleRef]) + return nil; + + return [PBGitRef refFromString:[self simpleRef]]; +} + +- (NSString *) description +{ + if (!description) + return [parameters componentsJoinedByString:@" "]; + + return description; +} + +- (void) setDescription:(NSString *)newDescription +{ + description = newDescription; +} + + +- (NSString *) title +{ + NSString *title = nil; + + if ([self.description isEqualToString:@"HEAD"]) + title = @"detached HEAD"; + else if ([self isSimpleRef]) + title = [[self ref] shortName]; + else if ([self.description hasPrefix:@"-S"]) + title = [self.description substringFromIndex:[@"-S" length]]; + else if ([self.description hasPrefix:@"HEAD -- "]) + title = [self.description substringFromIndex:[@"HEAD -- " length]]; + else if ([self.description hasPrefix:@"-- "]) + title = [self.description substringFromIndex:[@"-- " length]]; + else + title = self.description; + + return [NSString stringWithFormat:@"“%@”", title]; +} + +- (BOOL) hasPathLimiter; +{ + for (NSString* param in parameters) + if ([param isEqualToString:@"--"]) + return YES; + return NO; +} + +- (BOOL) isEqual:(PBGitRevSpecifier *)other +{ + if ([self isSimpleRef] ^ [other isSimpleRef]) + return NO; + + if ([self isSimpleRef]) + return [[[self parameters] objectAtIndex:0] isEqualToString:[other.parameters objectAtIndex:0]]; + + return [self.description isEqualToString:other.description]; +} + +- (NSUInteger) hash +{ + if ([self isSimpleRef]) + return [[[self parameters] objectAtIndex:0] hash]; + + return [self.description hash]; +} + +- (BOOL) isAllBranchesRev +{ + return [self isEqual:[PBGitRevSpecifier allBranchesRevSpec]]; +} + +- (BOOL) isLocalBranchesRev +{ + return [self isEqual:[PBGitRevSpecifier localBranchesRevSpec]]; +} + +- (void) encodeWithCoder:(NSCoder *)coder +{ + [coder encodeObject:description forKey:@"Description"]; + [coder encodeObject:parameters forKey:@"Parameters"]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + PBGitRevSpecifier *copy = [[[self class] allocWithZone:zone] initWithParameters:[self.parameters copy]]; + copy.description = [self.description copy]; + copy.workingDirectory = [self.workingDirectory copy]; + + return copy; +} + +@end diff --git a/Classes/git/PBGitSVBranchItem.h b/Classes/git/PBGitSVBranchItem.h new file mode 100644 index 000000000..f2eb18a6a --- /dev/null +++ b/Classes/git/PBGitSVBranchItem.h @@ -0,0 +1,18 @@ +// +// PBGitSVBranchItem.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBGitSVBranchItem : PBSourceViewItem { + +} + ++ (id)branchItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier; + +@end diff --git a/Classes/git/PBGitSVBranchItem.m b/Classes/git/PBGitSVBranchItem.m new file mode 100644 index 000000000..827d8e076 --- /dev/null +++ b/Classes/git/PBGitSVBranchItem.m @@ -0,0 +1,29 @@ +// +// PBGitSVBranchItem.m +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitSVBranchItem.h" +#import "PBGitRevSpecifier.h" + +@implementation PBGitSVBranchItem + + ++ (id)branchItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier +{ + PBGitSVBranchItem *item = [self itemWithTitle:[[revSpecifier description] lastPathComponent]]; + item.revSpecifier = revSpecifier; + + return item; +} + + +- (NSString*) iconName +{ + return @"BranchTemplate"; +} + +@end diff --git a/Classes/git/PBGitSVFolderItem.h b/Classes/git/PBGitSVFolderItem.h new file mode 100644 index 000000000..b71c22045 --- /dev/null +++ b/Classes/git/PBGitSVFolderItem.h @@ -0,0 +1,18 @@ +// +// PBGitSVFolderItem.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBGitSVFolderItem : PBSourceViewItem { + +} + ++ (id)folderItemWithTitle:(NSString *)title; + +@end diff --git a/Classes/git/PBGitSVFolderItem.m b/Classes/git/PBGitSVFolderItem.m new file mode 100644 index 000000000..66d958220 --- /dev/null +++ b/Classes/git/PBGitSVFolderItem.m @@ -0,0 +1,26 @@ +// +// PBGitSVFolderItem.m +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitSVFolderItem.h" + + +@implementation PBGitSVFolderItem + ++ (id)folderItemWithTitle:(NSString *)title +{ + PBGitSVFolderItem *item = [self itemWithTitle:title]; + + return item; +} + +- (NSString*) iconName +{ + return (self.isExpanded) ? @"FolderTemplate" : @"FolderClosedTemplate"; +} + +@end diff --git a/Classes/git/PBGitSVOtherRevItem.h b/Classes/git/PBGitSVOtherRevItem.h new file mode 100644 index 000000000..4b96a092d --- /dev/null +++ b/Classes/git/PBGitSVOtherRevItem.h @@ -0,0 +1,18 @@ +// +// PBGitSVOtherRevItem.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBGitSVOtherRevItem : PBSourceViewItem { + +} + ++ (id)otherItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier; + +@end diff --git a/Classes/git/PBGitSVOtherRevItem.m b/Classes/git/PBGitSVOtherRevItem.m new file mode 100644 index 000000000..b0854b412 --- /dev/null +++ b/Classes/git/PBGitSVOtherRevItem.m @@ -0,0 +1,29 @@ +// +// PBGitSVOtherRevItem.m +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitSVOtherRevItem.h" +#import "PBGitRevSpecifier.h" + + +@implementation PBGitSVOtherRevItem + + ++ (id)otherItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier +{ + PBGitSVOtherRevItem *item = [self itemWithTitle:[revSpecifier title]]; + item.revSpecifier = revSpecifier; + + return item; +} + +- (NSString*) iconName +{ + return @"BranchTemplate"; +} + +@end diff --git a/Classes/git/PBGitSVRemoteBranchItem.h b/Classes/git/PBGitSVRemoteBranchItem.h new file mode 100644 index 000000000..9e626b05a --- /dev/null +++ b/Classes/git/PBGitSVRemoteBranchItem.h @@ -0,0 +1,18 @@ +// +// PBGitSVRemoteBranchItem.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBGitSVRemoteBranchItem : PBSourceViewItem { + +} + ++ (id)remoteBranchItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier; + +@end diff --git a/Classes/git/PBGitSVRemoteBranchItem.m b/Classes/git/PBGitSVRemoteBranchItem.m new file mode 100644 index 000000000..4494069ae --- /dev/null +++ b/Classes/git/PBGitSVRemoteBranchItem.m @@ -0,0 +1,28 @@ +// +// PBGitSVRemoteBranchItem.m +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitSVRemoteBranchItem.h" +#import "PBGitRevSpecifier.h" + +@implementation PBGitSVRemoteBranchItem + + ++ (id)remoteBranchItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier +{ + PBGitSVRemoteBranchItem *item = [self itemWithTitle:[[revSpecifier description] lastPathComponent]]; + item.revSpecifier = revSpecifier; + + return item; +} + +- (NSString*) iconName +{ + return @"RemoteBranchTemplate"; +} + +@end diff --git a/Classes/git/PBGitSVRemoteItem.h b/Classes/git/PBGitSVRemoteItem.h new file mode 100644 index 000000000..ce6de1480 --- /dev/null +++ b/Classes/git/PBGitSVRemoteItem.h @@ -0,0 +1,18 @@ +// +// PBGitSVRemoteItem.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBGitSVRemoteItem : PBSourceViewItem { + +} + ++ (id)remoteItemWithTitle:(NSString *)title; + +@end diff --git a/Classes/git/PBGitSVRemoteItem.m b/Classes/git/PBGitSVRemoteItem.m new file mode 100644 index 000000000..88aeaeb7a --- /dev/null +++ b/Classes/git/PBGitSVRemoteItem.m @@ -0,0 +1,33 @@ +// +// PBGitSVRemoteItem.m +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitSVRemoteItem.h" +#import "PBGitRef.h" + + +@implementation PBGitSVRemoteItem + + ++ (id)remoteItemWithTitle:(NSString *)title +{ + PBGitSVRemoteItem *item = [self itemWithTitle:title]; + + return item; +} + +- (NSString*) iconName +{ + return @"RemoteTemplate"; +} + +- (PBGitRef *) ref +{ + return [PBGitRef refFromString:[kGitXRemoteRefPrefix stringByAppendingString:self.title]]; +} + +@end diff --git a/Classes/git/PBGitSVStageItem.h b/Classes/git/PBGitSVStageItem.h new file mode 100644 index 000000000..3ac90844a --- /dev/null +++ b/Classes/git/PBGitSVStageItem.h @@ -0,0 +1,18 @@ +// +// PBGitSVStageItem.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBGitSVStageItem : PBSourceViewItem { + +} + ++ (id) stageItem; + +@end diff --git a/Classes/git/PBGitSVStageItem.m b/Classes/git/PBGitSVStageItem.m new file mode 100644 index 000000000..8ea23edad --- /dev/null +++ b/Classes/git/PBGitSVStageItem.m @@ -0,0 +1,28 @@ +// +// PBGitSVStageItem.m +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitSVStageItem.h" + + +@implementation PBGitSVStageItem + + ++ (id) stageItem +{ + PBGitSVStageItem *item = [self itemWithTitle:@"Stage"]; + + return item; +} + + +- (NSString*) iconName +{ + return @"StageTemplate"; +} + +@end diff --git a/Classes/git/PBGitSVStashItem.h b/Classes/git/PBGitSVStashItem.h new file mode 100644 index 000000000..332a37a92 --- /dev/null +++ b/Classes/git/PBGitSVStashItem.h @@ -0,0 +1,19 @@ +// +// PBSourceViewStash.h +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import +#import "PBSourceViewItem.h" +#import "PBGitStash.h" + +@interface PBGitSVStashItem : PBSourceViewItem + ++ (id) itemWithStash:(PBGitStash*)stash; + +@property (nonatomic, strong) PBGitStash* stash; + +@end diff --git a/Classes/git/PBGitSVStashItem.m b/Classes/git/PBGitSVStashItem.m new file mode 100644 index 000000000..ccdd67829 --- /dev/null +++ b/Classes/git/PBGitSVStashItem.m @@ -0,0 +1,28 @@ +// +// PBSourceViewStash.m +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import "PBGitSVStashItem.h" +#import "PBGitRevSpecifier.h" + + +@implementation PBGitSVStashItem + ++ (id)itemWithStash:(PBGitStash *)stash +{ + NSString * title = [NSString stringWithFormat:@"@{%zd}: %@", stash.index, stash.message]; + PBGitSVStashItem * item = [self itemWithTitle:title]; + item.stash = stash; + item.revSpecifier = [[PBGitRevSpecifier alloc] initWithRef:stash.ref]; + return item; +} + +-(PBGitRef *)ref { + return self.stash.ref; +} + +@end diff --git a/Classes/git/PBGitSVSubmoduleItem.h b/Classes/git/PBGitSVSubmoduleItem.h new file mode 100644 index 000000000..bf1bf6a7b --- /dev/null +++ b/Classes/git/PBGitSVSubmoduleItem.h @@ -0,0 +1,17 @@ +// +// PBGitSVSubmoduleItem.h +// GitX +// +// Created by Seth Raphael on 9/14/12. +// +// + +#import +#import "PBSourceViewItem.h" + + +@interface PBGitSVSubmoduleItem : PBSourceViewItem ++ (id) itemWithSubmodule:(GTSubmodule*)submodule; +@property (nonatomic, strong) GTSubmodule* submodule; +@property (nonatomic, readonly) NSURL *path; +@end diff --git a/Classes/git/PBGitSVSubmoduleItem.m b/Classes/git/PBGitSVSubmoduleItem.m new file mode 100644 index 000000000..b78b083ca --- /dev/null +++ b/Classes/git/PBGitSVSubmoduleItem.m @@ -0,0 +1,31 @@ +// +// PBGitSVSubmoduleItem.m +// GitX +// +// Created by Seth Raphael on 9/14/12. +// +// + +#import "PBGitSVSubmoduleItem.h" + +@implementation PBGitSVSubmoduleItem + ++ (id) itemWithSubmodule:(GTSubmodule*)submodule +{ + PBGitSVSubmoduleItem* item = [[self alloc] init]; + item.submodule = submodule; + return item; +} + +- (NSString *)title +{ + return self.submodule.name; +} + +- (NSURL *)path +{ + NSURL *parentURL = self.submodule.parentRepository.fileURL; + NSURL *result = [parentURL URLByAppendingPathComponent:self.submodule.path]; + return result; +} +@end diff --git a/Classes/git/PBGitSVTagItem.h b/Classes/git/PBGitSVTagItem.h new file mode 100644 index 000000000..29e820adb --- /dev/null +++ b/Classes/git/PBGitSVTagItem.h @@ -0,0 +1,18 @@ +// +// PBGitSVTagItem.h +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import +#import "PBSourceViewItem.h" + +@interface PBGitSVTagItem : PBSourceViewItem { + +} + ++ (id)tagItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier; + +@end diff --git a/Classes/git/PBGitSVTagItem.m b/Classes/git/PBGitSVTagItem.m new file mode 100644 index 000000000..a13cfb1f7 --- /dev/null +++ b/Classes/git/PBGitSVTagItem.m @@ -0,0 +1,28 @@ +// +// PBGitSVTagItem.m +// GitX +// +// Created by Nathan Kinsinger on 3/2/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBGitSVTagItem.h" +#import "PBGitRevSpecifier.h" + +@implementation PBGitSVTagItem + + ++ (id)tagItemWithRevSpec:(PBGitRevSpecifier *)revSpecifier +{ + PBGitSVTagItem *item = [self itemWithTitle:[[revSpecifier description] lastPathComponent]]; + item.revSpecifier = revSpecifier; + + return item; +} + +- (NSString*) iconName +{ + return @"TagTemplate"; +} + +@end diff --git a/Classes/git/PBGitStash.h b/Classes/git/PBGitStash.h new file mode 100644 index 000000000..0f921b690 --- /dev/null +++ b/Classes/git/PBGitStash.h @@ -0,0 +1,27 @@ +// +// PBGitStash.h +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import +#import + +@class PBGitCommit; +@class PBGitRef; +@class PBGitRepository; + +@interface PBGitStash : NSObject +@property (nonatomic, readonly) NSInteger index; +@property (nonatomic, readonly) PBGitCommit *commit; +@property (nonatomic, readonly) NSString *message; +@property (nonatomic, readonly) PBGitRef *ref; + +@property (nonatomic, readonly) PBGitCommit *indexCommit; +@property (nonatomic, readonly) PBGitCommit *ancestorCommit; + +- (id)initWithRepository:(PBGitRepository *)repo stashOID:(GTOID *)stashOID index:(NSInteger)index message:(NSString *)message; + +@end diff --git a/Classes/git/PBGitStash.m b/Classes/git/PBGitStash.m new file mode 100644 index 000000000..93a268501 --- /dev/null +++ b/Classes/git/PBGitStash.m @@ -0,0 +1,49 @@ +// +// PBGitStash.m +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import "PBGitStash.h" +#import "PBGitRef.h" +#import "PBGitCommit.h" +#import "PBGitRepository.h" + +@implementation PBGitStash + +-(id)initWithRepository:(PBGitRepository *)repo stashOID:(GTOID *)stashOID index:(NSInteger)index message:(NSString *)message +{ + self = [self init]; + if (!self) return nil; + + _index = index; + _message = message; + + GTRepository * gtRepo = repo.gtRepo; + NSError * error = nil; + GTCommit * gtCommit = (GTCommit *)[gtRepo lookUpObjectByOID:stashOID objectType:GTObjectTypeCommit error:&error]; + NSArray * parents = [gtCommit parents]; + GTCommit * gtIndexCommit = [parents objectAtIndex:1]; + GTCommit * gtAncestorCommit = [parents objectAtIndex:0]; + + _commit = [[PBGitCommit alloc] initWithRepository:repo andCommit:gtCommit]; + _indexCommit = [[PBGitCommit alloc] initWithRepository:repo andCommit:gtIndexCommit]; + _ancestorCommit = [[PBGitCommit alloc] initWithRepository:repo andCommit:gtAncestorCommit]; + + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"stash@{%zd}: %@", _index, _message]; +} + +- (PBGitRef *)ref +{ + NSString * refStr = [NSString stringWithFormat:@"refs/stash@{%zd}", _index]; + return [[PBGitRef alloc] initWithString:refStr]; +} + +@end diff --git a/PBGitTree.h b/Classes/git/PBGitTree.h similarity index 78% rename from PBGitTree.h rename to Classes/git/PBGitTree.h index d48ab6e57..7efbbb31e 100644 --- a/PBGitTree.h +++ b/Classes/git/PBGitTree.h @@ -7,15 +7,14 @@ // #import -#import "PBGitRepository.h" + +@class PBGitRepository; @interface PBGitTree : NSObject { long long _fileSize; NSString* sha; NSString* path; - PBGitRepository* repository; - __weak PBGitTree* parent; NSArray* children; BOOL leaf; @@ -26,6 +25,9 @@ + (PBGitTree*) rootForCommit: (id) commit; + (PBGitTree*) treeForTree: (PBGitTree*) tree andPath: (NSString*) path; - (void) saveToFolder: (NSString *) directory; +- (NSString *)textContents; +- (NSString *)blame; +- (NSString *) log:(NSString *)format; - (NSString*) tmpFileNameForContents; - (long long)fileSize; @@ -33,8 +35,8 @@ @property(copy) NSString* sha; @property(copy) NSString* path; @property(assign) BOOL leaf; -@property(retain) PBGitRepository* repository; -@property(assign) __weak PBGitTree* parent; +@property(nonatomic, weak) PBGitRepository* repository; +@property(nonatomic, weak) PBGitTree* parent; @property(readonly) NSArray* children; @property(readonly) NSString* fullPath; diff --git a/PBGitTree.m b/Classes/git/PBGitTree.m similarity index 51% rename from PBGitTree.m rename to Classes/git/PBGitTree.m index c8003d1a0..0cbf2169e 100644 --- a/PBGitTree.m +++ b/Classes/git/PBGitTree.m @@ -6,10 +6,11 @@ // Copyright 2008 __MyCompanyName__. All rights reserved. // +#import "PBGitRepository.h" +#import "PBGitRepository_PBGitBinarySupport.h" #import "PBGitTree.h" #import "PBGitCommit.h" #import "NSFileHandleExt.h" -#import "PBEasyPipe.h" #import "PBEasyFS.h" @implementation PBGitTree @@ -22,7 +23,7 @@ + (PBGitTree*) rootForCommit:(id) commit PBGitTree* tree = [[self alloc] init]; tree.parent = nil; tree.leaf = NO; - tree.sha = [c realSha]; + tree.sha = c.SHA; tree.repository = c.repository; tree.path = @""; return tree; @@ -65,36 +66,42 @@ - (BOOL) isLocallyCached - (BOOL)hasBinaryHeader:(NSString*)contents { - if(!contents) + if (!contents) return NO; - return [contents rangeOfString:@"\0" options:0 range:NSMakeRange(0, ([contents length] >= 8000) ? 7999 : [contents length])].location != NSNotFound; + return [contents rangeOfString:@"\0" + options:0 + range:NSMakeRange(0, ([contents length] >= 8000) ? 7999 : [contents length])].location != NSNotFound; } - (BOOL)hasBinaryAttributes { - // First ask git check-attr if the file has a binary attribute custom set - NSFileHandle *handle = [repository handleInWorkDirForArguments:[NSArray arrayWithObjects:@"check-attr", @"binary", [self fullPath], nil]]; - NSData *data = [handle readDataToEndOfFile]; - NSString *string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; - - if (!string) + @try { + // First ask git check-attr if the file has a binary attribute custom set + NSError *error = nil; + NSString *string = [repository outputOfTaskWithArguments:@[@"check-attr", @"binary", self.fullPath] error:&error]; + + if (!string) + return NO; + string = [string stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + + if ([string hasSuffix:@"binary: set"]) + return YES; + + if ([string hasSuffix:@"binary: unset"]) + return NO; + + // Binary state unknown, do a check on common filename-extensions + for (NSString *extension in [NSArray arrayWithObjects:@".pdf", @".jpg", @".jpeg", @".png", @".bmp", @".gif", @".o", nil]) { + if ([[self fullPath] hasSuffix:extension]) + return YES; + } + return NO; - string = [string stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - - if ([string hasSuffix:@"binary: set"]) - return YES; - - if ([string hasSuffix:@"binary: unset"]) + } + @catch (NSException *exception) { return NO; - - // Binary state unknown, do a check on common filename-extensions - for (NSString *extension in [NSArray arrayWithObjects:@".pdf", @".jpg", @".jpeg", @".png", @".bmp", @".gif", @".o", nil]) { - if ([[self fullPath] hasSuffix:extension]) - return YES; } - - return NO; } - (NSString*) contents @@ -109,8 +116,55 @@ - (NSString*) contents string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; return string; } + + return [repository outputOfTaskWithArguments:@[@"show", self.refSpec] error:NULL]; +} + +- (NSString *) blame +{ + if (!leaf) + return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]]; + + if ([self hasBinaryAttributes]) + return [NSString stringWithFormat:@"%@ appears to be a binary file of %lld bytes", [self fullPath], [self fileSize]]; + + if ([self fileSize] > 52428800) // ~50MB + return [NSString stringWithFormat:@"%@ is too big to be displayed (%lld bytes)", [self fullPath], [self fileSize]]; + + NSString *contents = [repository outputOfTaskWithArguments:@[@"blame", @"-p", sha, @"--", self.fullPath] error:NULL]; - return [repository outputForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]]; + if ([self hasBinaryHeader:contents]) + return [NSString stringWithFormat:@"%@ appears to be a binary file of %lld bytes", [self fullPath], [self fileSize]]; + + + return contents; +} + +- (NSString *) log:(NSString *)format +{ + if (!leaf) + return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]]; + + if ([self hasBinaryAttributes]) + return [NSString stringWithFormat:@"%@ appears to be a binary file of %lld bytes", [self fullPath], [self fileSize]]; + + if ([self fileSize] > 52428800) // ~50MB + return [NSString stringWithFormat:@"%@ is too big to be displayed (%lld bytes)", [self fullPath], [self fileSize]]; + + NSArray *arguments = @[ + @"log", + [NSString stringWithFormat:@"--pretty=format:%@", format], + @"--follow", + @"--", + [self fullPath], + ]; + NSString *contents = [repository outputOfTaskWithArguments:arguments error:NULL]; + + if ([self hasBinaryHeader:contents]) + return [NSString stringWithFormat:@"%@ appears to be a binary file of %lld bytes", [self fullPath], [self fileSize]]; + + + return contents; } - (long long)fileSize @@ -118,8 +172,7 @@ - (long long)fileSize if (_fileSize) return _fileSize; - NSFileHandle *handle = [repository handleForArguments:[NSArray arrayWithObjects:@"cat-file", @"-s", [self refSpec], nil]]; - NSString *sizeString = [[NSString alloc] initWithData:[handle readDataToEndOfFile] encoding:NSISOLatin1StringEncoding]; + NSString *sizeString = [repository outputOfTaskWithArguments:@[@"cat-file", @"-s", self.refSpec] error:NULL]; if (!sizeString) _fileSize = -1; @@ -135,15 +188,15 @@ - (NSString *)textContents return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]]; if ([self hasBinaryAttributes]) - return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]]; + return [NSString stringWithFormat:@"%@ appears to be a binary file of %lld bytes", [self fullPath], [self fileSize]]; if ([self fileSize] > 52428800) // ~50MB - return [NSString stringWithFormat:@"%@ is too big to be displayed (%d bytes)", [self fullPath], [self fileSize]]; + return [NSString stringWithFormat:@"%@ is too big to be displayed (%lld bytes)", [self fullPath], [self fileSize]]; NSString* contents = [self contents]; if ([self hasBinaryHeader:contents]) - return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]]; + return [NSString stringWithFormat:@"%@ appears to be a binary file of %lld bytes", [self fullPath], [self fileSize]]; return contents; } @@ -153,11 +206,17 @@ - (void) saveToFolder: (NSString *) dir NSString* newName = [dir stringByAppendingPathComponent:path]; if (leaf) { - NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]]; - NSData* data = [handle readDataToEndOfFile]; - [data writeToFile:newName atomically:YES]; + PBTask *task = [repository taskWithArguments:@[@"show", self.refSpec]]; + + [task launchTask:NULL]; + + [task.standardOutputData writeToFile:newName atomically:YES]; } else { // Directory - [[NSFileManager defaultManager] createDirectoryAtPath:newName attributes:nil]; + NSError *error = nil; + if (![[NSFileManager defaultManager] createDirectoryAtPath:newName withIntermediateDirectories:YES attributes:nil error:&error]) { + NSLog(@"Error creating directory %@: %@", newName, error); + return; + } for (PBGitTree* child in [self children]) [child saveToFolder: newName]; } @@ -190,10 +249,13 @@ - (NSString*) tmpFileNameForContents if (!localFileName) localFileName = [[PBEasyFS tmpDirWithPrefix: sha] stringByAppendingPathComponent:path]; - - NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]]; - NSData* data = [handle readDataToEndOfFile]; - [data writeToFile:localFileName atomically:YES]; + + + PBTask *task = [repository taskWithArguments:@[@"show", self.refSpec]]; + + [task launchTask:NULL]; + + [task.standardOutputData writeToFile:localFileName atomically:YES]; NSFileManager* fs = [NSFileManager defaultManager]; localMtime = [[fs attributesOfItemAtPath:localFileName error: nil] objectForKey:NSFileModificationDate]; @@ -226,6 +288,11 @@ - (NSArray*) children p = [handle readLine]; } + [c sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { + PBGitTree* tree1 = (PBGitTree*)obj1; + PBGitTree* tree2 = (PBGitTree*)obj2; + return [[tree1 path] localizedStandardCompare:[tree2 path]]; + }]; children = c; return c; } @@ -241,10 +308,13 @@ - (NSString*) fullPath return [parent.fullPath stringByAppendingPathComponent: self.path]; } -- (void) finalize +- (void) dealloc { - if (localFileName) - [[NSFileManager defaultManager] removeFileAtPath:localFileName handler:nil]; - [super finalize]; + if (localFileName) { + NSError *error = nil; + if (![[NSFileManager defaultManager] removeItemAtPath:localFileName error:&error]) { + NSLog(@"Failed to remove item %@: %@", localFileName, error); + } + } } @end diff --git a/PBGitXProtocol.h b/Classes/git/PBGitXProtocol.h similarity index 57% rename from PBGitXProtocol.h rename to Classes/git/PBGitXProtocol.h index c4790c3ec..f048b1aa4 100644 --- a/PBGitXProtocol.h +++ b/Classes/git/PBGitXProtocol.h @@ -7,18 +7,16 @@ // #import -#import "PBGitRepository.h" +@class PBGitRepository; -@interface PBGitXProtocol : NSURLProtocol { - NSFileHandle *handle; -} +@interface PBGitXProtocol : NSURLProtocol @end @interface NSURLRequest (PBGitXProtocol) -@property (readonly) PBGitRepository *repository; +@property (nonatomic, strong) PBGitRepository *repository; @end @interface NSMutableURLRequest (PBGitXProtocol) -@property (retain) PBGitRepository *repository; +@property (nonatomic, strong) PBGitRepository *repository; @end diff --git a/PBGitXProtocol.m b/Classes/git/PBGitXProtocol.m similarity index 66% rename from PBGitXProtocol.m rename to Classes/git/PBGitXProtocol.m index dcffeeaad..99b821cf0 100644 --- a/PBGitXProtocol.m +++ b/Classes/git/PBGitXProtocol.m @@ -7,13 +7,23 @@ // #import "PBGitXProtocol.h" +#import "PBGitRepository.h" +#import "PBGitRepository_PBGitBinarySupport.h" +@interface PBGitXProtocol () { + PBTask *_task; +} +@end @implementation PBGitXProtocol + (BOOL) canInitWithRequest:(NSURLRequest *)request { - return [[[request URL] scheme] isEqualToString:@"GitX"]; + NSString *URLScheme = request.URL.scheme; + if ([[URLScheme lowercaseString] isEqualToString:@"gitx"]) + return YES; + + return NO; } + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request @@ -26,15 +36,22 @@ -(void)startLoading NSURL *url = [[self request] URL]; PBGitRepository *repo = [[self request] repository]; - if(!repo) { + if (!repo) { [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:nil]]; return; } NSString *specifier = [NSString stringWithFormat:@"%@:%@", [url host], [[url path] substringFromIndex:1]]; - handle = [repo handleInWorkDirForArguments:[NSArray arrayWithObjects:@"cat-file", @"blob", specifier, nil]]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishFileLoad:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - [handle readToEndOfFileInBackgroundAndNotify]; + _task = [repo taskWithArguments:@[@"cat-file", @"blob", specifier]]; + [_task performTaskWithCompletionHandler:^(NSData *readData, NSError *error) { + if (error) { + [[self client] URLProtocol:self didFailWithError:error]; + return; + } + + [[self client] URLProtocol:self didLoadData:readData]; + [[self client] URLProtocolDidFinishLoading:self]; + }]; NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[[self request] URL] MIMEType:nil @@ -46,21 +63,15 @@ -(void)startLoading cacheStoragePolicy:NSURLCacheStorageNotAllowed]; } -- (void) didFinishFileLoad:(NSNotification *)notification -{ - NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem]; - [[self client] URLProtocol:self didLoadData:data]; - [[self client] URLProtocolDidFinishLoading:self]; -} - - (void) stopLoading { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_task terminate]; } @end @implementation NSURLRequest (PBGitXProtocol) +@dynamic repository; - (PBGitRepository *) repository { diff --git a/Classes/git/PBRepositoryFinder.h b/Classes/git/PBRepositoryFinder.h new file mode 100644 index 000000000..13e8d86e1 --- /dev/null +++ b/Classes/git/PBRepositoryFinder.h @@ -0,0 +1,17 @@ +// +// PBRepositoryFinder.h +// GitX +// +// Created by Rowan James on 13/11/2012. +// +// + +#import + +@interface PBRepositoryFinder : NSObject + ++ (NSURL *)fileURLForURL:(NSURL *)inputURL; ++ (NSURL*)workDirForURL:(NSURL*)fileURL; ++ (NSURL*)gitDirForURL:(NSURL*)fileURL; + +@end 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 new file mode 100644 index 000000000..eccb89e1f --- /dev/null +++ b/Classes/gitx.m @@ -0,0 +1,382 @@ +// +// gitx.m +// GitX +// +// Created by Ciarán Walsh on 15/08/2008. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBRepositoryFinder.h" +#import "GitXScriptingConstants.h" +#import "GitX.h" +#import "PBHistorySearchController.h" + + +#pragma mark Commands handled locally + +void usage(char const *programName) +{ + + printf("Usage: %s (--help|--version|--git-path)\n", programName); + printf(" or: %s (--commit)\n", programName); + printf(" or: %s (--all|--local|--branch) [branch/tag]\n", programName); + printf(" or: %s \n", programName); + printf(" or: %s (--diff)\n", programName); + printf(" or: %s (--init)\n", programName); + printf(" or: %s (--clone [destination])\n", programName); + printf("\n"); + printf(" -h, --help print this help\n"); + printf(" -v, --version prints version info for both GitX and git\n"); + printf(" --git-path prints the path to the directory containing git\n"); + printf("\n"); + printf("Repository path\n"); + printf(" By default gitx opens the repository in the current directory.\n"); + printf(" Use --git-dir= to send commands to a repository somewhere else.\n"); + printf(" Note: This must be the first argument.\n"); + printf("\n"); + printf(" --git-dir= [gitx commands]\n"); + printf(" send the gitx commands to the repository located at \n"); + printf("\n"); + printf("Commit/Stage view\n"); + printf(" -c, --commit start GitX in commit/stage mode\n"); + printf("\n"); + printf("Branch filter options\n"); + printf(" Add an optional branch or tag name to select that branch using the given branch filter\n"); + printf("\n"); + printf(" --all [branch] view history for all branches\n"); + printf(" --local [branch] view history for local branches only\n"); + printf(" --branch [branch] view history for the selected branch only\n"); + printf("\n"); + printf("RevList options\n"); + printf(" See 'man git-log' and 'man git-rev-list' for options you can pass to gitx\n"); + printf("\n"); + printf(" select specific branch or tag\n"); + printf(" -- show commits touching paths\n"); + printf("\n"); + printf("Diff options\n"); + printf(" See 'man git-diff' for options you can pass to gitx --diff\n"); + printf("\n"); + printf(" -d, --diff [] {0,2} [--] [...]\n"); + printf(" shows the diff in a window in GitX\n"); + printf(" git diff [options] | gitx\n"); + printf(" use gitx to pipe diff output to a GitX window\n"); + printf("\n"); + printf("Search\n"); + printf("\n"); + printf(" -s, --search=\n"); + printf(" search for string in Subject, Author or SHA\n"); + printf(" -S, --Search=\n"); + printf(" commits that introduce or remove an instance of \n"); + printf(" -r, --regex=\n"); + printf(" commits that introduce or remove strings that match \n"); + printf(" -p, --path=\n"); + printf(" commits that modify the file at file path\n"); + printf("\n"); + printf("Creating repositories\n"); + printf(" These commands will create a git repository and then open it up in GitX\n"); + printf("\n"); + printf(" --init creates (or reinitializes) a git repository\n"); + printf(" --clone [destination path]\n"); + printf(" clones the repository (at the specified URL) into the current\n"); + printf(" directory or into the specified path\n"); + printf("\n"); + exit(1); +} + +void version_info() +{ + NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; + printf("GitX version %s\n", [version UTF8String]); + exit(1); +} + +#pragma mark - +#pragma mark Commands sent to GitX + +void handleSTDINDiff() +{ + NSFileHandle *handle = [NSFileHandle fileHandleWithStandardInput]; + NSData *data = [handle readDataToEndOfFile]; + NSString *diff = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + if (diff && [diff length] > 0) { + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp setSendMode:kAENoReply]; + [gitXApp activate]; + [gitXApp showDiff:diff]; + exit(0); + } +} + +void handleDiffWithArguments(NSURL *repositoryURL, NSArray *arguments) +{ + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp setSendMode:kAENoReply]; + [gitXApp activate]; + [gitXApp performDiffIn:repositoryURL withOptions:arguments]; + exit(0); +} + +void handleOpenRepository(NSURL *repositoryURL, NSArray *arguments) +{ + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp setSendMode:kAENoReply]; + [gitXApp activate]; + [gitXApp open:repositoryURL withOptions:arguments]; + return; +} + +void handleInit(NSURL *repositoryURL) +{ + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp createRepository:repositoryURL]; + + exit(0); +} + +void handleClone(NSURL *repositoryURL, NSMutableArray *arguments) +{ + if ([arguments count]) { + NSString *repository = [arguments objectAtIndex:0]; + + if ([arguments count] > 1) { + NSURL *url = [NSURL fileURLWithPath:[arguments objectAtIndex:1]]; + if (url) + repositoryURL = url; + } + + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp cloneRepository:repository to:repositoryURL isBare:NO]; + } + else { + printf("Error: --clone needs the URL of the repository to clone.\n"); + exit(2); + } + + + exit(0); +} + +#define kShortBasicSearch @"-s" +#define kBasicSearch @"--search=" +#define kShortPickaxeSearch @"-S" +#define kPickaxeSearch @"--Search=" +#define kShortRegexSearch @"-r" +#define kRegexSearch @"--regex=" +#define kShortPathSearch @"-p" +#define kPathSearch @"--path=" + +NSArray *commandLineSearchPrefixes() +{ + return [NSArray arrayWithObjects:kShortBasicSearch, kBasicSearch, kShortPickaxeSearch, kPickaxeSearch, kShortRegexSearch, kRegexSearch, kShortPathSearch, kPathSearch, nil]; +} + +PBHistorySearchMode searchModeForCommandLineArgument(NSString *argument) +{ + if ([argument hasPrefix:kShortBasicSearch] || [argument hasPrefix:kBasicSearch]) + return PBHistorySearchModeBasic; + + if ([argument hasPrefix:kShortPickaxeSearch] || [argument hasPrefix:kPickaxeSearch]) + return PBHistorySearchModePickaxe; + + if ([argument hasPrefix:kShortRegexSearch] || [argument hasPrefix:kRegexSearch]) + return PBHistorySearchModeRegex; + + if ([argument hasPrefix:kShortPathSearch] || [argument hasPrefix:kPathSearch]) + return PBHistorySearchModePath; + + return 0; +} + +GitXDocument *documentForURL(SBElementArray *documents, NSURL *theURL) +{ + for (GitXDocument *document in documents) + { + NSURL* docURL = [document file]; + if ([docURL isEqualTo:theURL]) + { + return document; + } + } + return nil; +} + +void handleGitXSearch(NSURL *repositoryURL, PBHistorySearchMode mode, NSMutableArray *arguments) +{ + NSString *searchString = [arguments componentsJoinedByString:@" "]; + + // remove the prefix from search string before sending it + NSArray *prefixes = commandLineSearchPrefixes(); + for (NSString *prefix in prefixes) + if ([searchString hasPrefix:prefix]) { + searchString = [searchString substringFromIndex:[prefix length]]; + break; + } + + if ([searchString isEqualToString:@""]) + exit(0); + + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp open:[NSArray arrayWithObject:repositoryURL]]; + + // need to find the document after opening it + GitXDocument *repositoryDocument = documentForURL([gitXApp documents], repositoryURL); + [repositoryDocument searchString:searchString inMode:mode]; + + exit(0); +} + + +#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; + } + + return [NSURL fileURLWithPath:path]; +} + +NSURL *workingDirectoryURL(NSMutableArray *arguments) +{ + 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]; + } + + NSURL *url = checkWorkingDirectoryPath(path); + + // 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; + } + + // 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]; + + // 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 + + // Valid repository found, lets' drop parsed argument + [arguments removeObjectAtIndex:i]; + + 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) +{ + @autoreleasepool { + if (argc >= 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) + usage(argv[0]); + if (argc >= 2 && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v"))) + version_info(); + 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 + NSURL *wdURL = workingDirectoryURL(arguments); + if (!wdURL) + { + printf("Could not find a git working directory.\n"); + exit(0); + } + + if ([arguments count]) { + NSString *firstArgument = [arguments objectAtIndex:0]; + + if ([firstArgument isEqualToString:@"--diff"] || [firstArgument isEqualToString:@"-d"]) { + [arguments removeObjectAtIndex:0]; + handleDiffWithArguments(wdURL, arguments); + } + + if ([firstArgument isEqualToString:@"--init"]) { + [arguments removeObjectAtIndex:0]; + handleInit(wdURL); + } + + if ([firstArgument isEqualToString:@"--clone"]) { + [arguments removeObjectAtIndex:0]; + handleClone(wdURL, arguments); + } + + PBHistorySearchMode mode; + if ((mode = searchModeForCommandLineArgument(firstArgument))) { + handleGitXSearch(wdURL, mode, arguments); + } + + if ([firstArgument isEqualToString:@"--"]) { + [arguments removeObjectAtIndex:0]; + handleGitXSearch(wdURL, PBHistorySearchModePath, arguments); + } + } + + // No commands handled by gitx, open the current dir in GitX with the arguments + handleOpenRepository(wdURL, arguments); + + return 0; + } +} diff --git a/Classes/gitx_askpasswd_main.m b/Classes/gitx_askpasswd_main.m new file mode 100644 index 000000000..7257aa183 --- /dev/null +++ b/Classes/gitx_askpasswd_main.m @@ -0,0 +1,181 @@ +/* + * gitx_askpasswd_main.m + * GitX + * + * Created by Uli Kusterer on 19.02.10. + * Copyright 2010 The Void Software. All rights reserved. + * + */ + +#include +#import + +#define OKBUTTONWIDTH 100.0 +#define OKBUTTONHEIGHT 24.0 +#define CANCELBUTTONWIDTH 100.0 +#define CANCELBUTTONHEIGHT 24.0 +#define PASSHEIGHT 22.0 +#define PASSLABELHEIGHT 16.0 +#define WINDOWAUTOSAVENAME @"GitXAskPasswordWindowFrame" + + +@interface GAPAppDelegate : NSObject +{ + NSPanel* mPasswordPanel; + NSSecureTextField* mPasswordField; +} + +-(NSPanel*) passwordPanel; + +-(IBAction) doOKButton: (id)sender; +-(IBAction) doCancelButton: (id)sender; + +@end + + +@implementation GAPAppDelegate + +static const NSInteger kReturnCodeOK = 0; +static const NSInteger kReturnCodeCancel = 1; + + +-(NSPanel*) passwordPanel +{ + if( !mPasswordPanel ) + { + NSRect box = NSMakeRect( 100, 100, 400, 134 ); + mPasswordPanel = [[NSPanel alloc] initWithContentRect: box + styleMask: NSWindowStyleMaskTitled + backing: NSBackingStoreBuffered defer: NO]; + [mPasswordPanel setHidesOnDeactivate: NO]; + [mPasswordPanel setLevel: NSFloatingWindowLevel]; + [mPasswordPanel setTitle: NSLocalizedString(@"GitX SSH Remote Login", @"Title for password panel in command line tool")]; + if (![mPasswordPanel setFrameUsingName: WINDOWAUTOSAVENAME]) { + [mPasswordPanel center]; + [mPasswordPanel setFrameAutosaveName: WINDOWAUTOSAVENAME]; + } + + box.origin = NSZeroPoint; // Only need local coords from now on. + + // OK: + NSRect okBox = box; + okBox.origin.x = NSMaxX( box ) -OKBUTTONWIDTH -20; + okBox.size.width = OKBUTTONWIDTH; + okBox.origin.y += 20; + okBox.size.height = OKBUTTONHEIGHT; + NSButton *okButton = [[NSButton alloc] initWithFrame: okBox]; + [okButton setTarget: self]; + [okButton setAction: @selector(doOKButton:)]; + [okButton setTitle: NSLocalizedString(@"OK", @"OK button for password panel in command line tool")]; + [okButton setKeyEquivalent: @"\r"]; + [okButton setBordered: YES]; + [okButton setBezelStyle: NSRoundedBezelStyle]; + [[mPasswordPanel contentView] addSubview: okButton]; + + // Cancel: + NSRect cancelBox = box; + cancelBox.origin.x = NSMinX( okBox ) -CANCELBUTTONWIDTH -6; + cancelBox.size.width = CANCELBUTTONWIDTH; + cancelBox.origin.y += 20; + cancelBox.size.height = CANCELBUTTONHEIGHT; + NSButton *cancleButton = [[NSButton alloc] initWithFrame: cancelBox]; + [cancleButton setTarget: self]; + [cancleButton setAction: @selector(doCancelButton:)]; + [cancleButton setTitle: NSLocalizedString(@"Cancel", @"Cancel button for password panel in command line tool")]; + [cancleButton setBordered: YES]; + [cancleButton setBezelStyle: NSRoundedBezelStyle]; + [[mPasswordPanel contentView] addSubview: cancleButton]; + + // Password field: + NSRect passBox = box; + passBox.origin.y = NSMaxY(okBox) + 24; + passBox.size.height = PASSHEIGHT; + passBox.origin.x += 104; + passBox.size.width -= 104 + 20; + mPasswordField = [[NSSecureTextField alloc] initWithFrame: passBox]; + [mPasswordField setSelectable: YES]; + [mPasswordField setEditable: YES]; + [mPasswordField setBordered: YES]; + [mPasswordField setBezeled: YES]; + [mPasswordField setBezelStyle: NSTextFieldSquareBezel]; + [mPasswordField selectText: self]; + [[mPasswordPanel contentView] addSubview: mPasswordField]; + + // Password label: + NSRect passLabelBox = box; + passLabelBox.origin.y = NSMaxY(passBox) + 8; + passLabelBox.size.height = PASSLABELHEIGHT; + passLabelBox.origin.x += 100; + passLabelBox.size.width -= 100 + 20; + NSTextField *passwordLabel = [[NSTextField alloc] initWithFrame: passLabelBox]; + [passwordLabel setSelectable: YES]; + [passwordLabel setEditable: NO]; + [passwordLabel setBordered: NO]; + [passwordLabel setBezeled: NO]; + [passwordLabel setDrawsBackground: NO]; + [passwordLabel setStringValue: NSLocalizedString(@"Please enter your password:", @"Label for password field in password panel in command line tool")]; + [[mPasswordPanel contentView] addSubview: passwordLabel]; + + // GitX icon: + NSRect gitxIconBox = box; + gitxIconBox.origin.y = NSMaxY(box) - 78; + gitxIconBox.size.height = 64; + gitxIconBox.origin.x += 20; + gitxIconBox.size.width = 64; + NSImageView *gitxIconView = [[NSImageView alloc] initWithFrame: gitxIconBox]; + [gitxIconView setEditable: NO]; + NSString *gitxIconPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"gitx.icns"]; + NSImage *gitxIcon = [[NSImage alloc] initWithContentsOfFile: gitxIconPath]; + [gitxIconView setImage: gitxIcon]; + [[mPasswordPanel contentView] addSubview: gitxIconView]; + } + + return mPasswordPanel; +} + + +-(IBAction) doOKButton: (id)sender +{ + printf( "%s\n", [[mPasswordField stringValue] UTF8String] ); + [[NSApplication sharedApplication] stopModalWithCode:kReturnCodeOK]; +} + + +// TODO: Need to find out how to get SSH to cancel. +// When the user cancels the window it is opened again for however +// many times the remote server allows failed attempts. +-(IBAction) doCancelButton: (id)sender +{ + [[NSApplication sharedApplication] stopModalWithCode:kReturnCodeCancel]; +} + +@end + + + +int main( int argc, const char** argv ) +{ + @autoreleasepool { + // close stderr to stop cocoa log messages from being picked up by GitX + close(STDERR_FILENO); + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + ProcessSerialNumber myPSN = { 0, kCurrentProcess }; + TransformProcessType( &myPSN, kProcessTransformToForegroundApplication ); + + NSApplication *app = [NSApplication sharedApplication]; + GAPAppDelegate *appDel = [[GAPAppDelegate alloc] init]; + [app setDelegate: appDel]; + NSWindow *passPanel = [appDel passwordPanel]; + + [app activateIgnoringOtherApps: YES]; + [passPanel makeKeyAndOrderFront: nil]; + NSInteger code = [app runModalForWindow: passPanel]; + + [defaults synchronize]; + + return code == kReturnCodeOK ? EXIT_SUCCESS : EXIT_FAILURE; + } +} + diff --git a/main.m b/Classes/main.m similarity index 55% rename from main.m rename to Classes/main.m index 93e35cb9f..6f0a379f4 100644 --- a/main.m +++ b/Classes/main.m @@ -8,7 +8,11 @@ #import -int main(int argc, char *argv[]) +int main(int argc, const char **argv) { - return NSApplicationMain(argc, (const char **) argv); + int result; + @autoreleasepool { + result = NSApplicationMain(argc, argv); + } + return result; } diff --git a/Credits.html b/Credits.html deleted file mode 100644 index e4ebc96d0..000000000 --- a/Credits.html +++ /dev/null @@ -1 +0,0 @@ -GitX Homepage \ No newline at end of file diff --git a/DBPrefsWindowController.h b/DBPrefsWindowController.h deleted file mode 100644 index cc208de58..000000000 --- a/DBPrefsWindowController.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// DBPrefsWindowController.h -// -// Created by Dave Batton -// http://www.Mere-Mortal-Software.com/blog/ -// -// Documentation for this class is available here: -// http://www.mere-mortal-software.com/blog/details.php?d=2007-03-11 -// -// Copyright 2007. Some rights reserved. -// This work is licensed under a Creative Commons license: -// http://creativecommons.org/licenses/by/3.0/ -// -// 11 March 2007 : Initial 1.0 release -// 15 March 2007 : Version 1.1 -// Resizing is now handled along with the cross-fade by -// the NSViewAnimation routine. -// Cut the fade time in half to speed up the window resize. -// -setupToolbar is now called each time the window opens so -// you can configure it differently each time if you want. -// Holding down the shift key will now slow down the animation. -// This can be disabled by using the new -setShiftSlowsAnimation: -// method. -// 23 March 2007 : Version 1.1.1 -// The initial first responder now gets set when the view is -// swapped so that the user can tab to the objects displayed -// in the window. -// Also added a work-around to Cocoa's insistance on drawing -// a focus ring around the first toolbar icon when going from -// a view with a focusable item to a view without a focusable item. -// -// 31 May 2007 : Version 1.1.2 -// The window's title bar and toolbar heights are now calculated at -// runtime, rather than being hard-coded. -// Fixed a redraw problem and a window placement problem associated -// with large preference windows. -// Added some code to supress compiler warnings from unused parameters. -// Fixed a couple of objects that weren't being properly released. -// - - -#import - - -@interface DBPrefsWindowController : NSWindowController { - NSMutableArray *toolbarIdentifiers; - NSMutableDictionary *toolbarViews; - NSMutableDictionary *toolbarItems; - - BOOL _crossFade; - BOOL _shiftSlowsAnimation; - - NSView *contentSubview; - NSViewAnimation *viewAnimation; -} - - -+ (DBPrefsWindowController *)sharedPrefsWindowController; -+ (NSString *)nibName; - -- (void)setupToolbar; -- (void)addView:(NSView *)view label:(NSString *)label; -- (void)addView:(NSView *)view label:(NSString *)label image:(NSImage *)image; - -- (BOOL)crossFade; -- (void)setCrossFade:(BOOL)fade; -- (BOOL)shiftSlowsAnimation; -- (void)setShiftSlowsAnimation:(BOOL)slows; - -- (void)displayViewForIdentifier:(NSString *)identifier animate:(BOOL)animate; -- (void)crossFadeView:(NSView *)oldView withView:(NSView *)newView; -- (NSRect)frameForView:(NSView *)view; - - -@end diff --git a/DBPrefsWindowController.m b/DBPrefsWindowController.m deleted file mode 100644 index 3988a99a3..000000000 --- a/DBPrefsWindowController.m +++ /dev/null @@ -1,409 +0,0 @@ -// -// DBPrefsWindowController.m -// - -#import "DBPrefsWindowController.h" - - -static DBPrefsWindowController *_sharedPrefsWindowController = nil; - - -@implementation DBPrefsWindowController - - - - -#pragma mark - -#pragma mark Class Methods - - -+ (DBPrefsWindowController *)sharedPrefsWindowController -{ - if (!_sharedPrefsWindowController) { - _sharedPrefsWindowController = [[self alloc] initWithWindowNibName:[self nibName]]; - } - return _sharedPrefsWindowController; -} - - - - -+ (NSString *)nibName - // Subclasses can override this to use a nib with a different name. -{ - return @"Preferences"; -} - - - - -#pragma mark - -#pragma mark Setup & Teardown - - -- (id)initWithWindow:(NSWindow *)window - // -initWithWindow: is the designated initializer for NSWindowController. -{ - self = [super initWithWindow:nil]; - if (self != nil) { - // Set up an array and some dictionaries to keep track - // of the views we'll be displaying. - toolbarIdentifiers = [[NSMutableArray alloc] init]; - toolbarViews = [[NSMutableDictionary alloc] init]; - toolbarItems = [[NSMutableDictionary alloc] init]; - - // Set up an NSViewAnimation to animate the transitions. - viewAnimation = [[NSViewAnimation alloc] init]; - [viewAnimation setAnimationBlockingMode:NSAnimationNonblocking]; - [viewAnimation setAnimationCurve:NSAnimationEaseInOut]; - [viewAnimation setDelegate:self]; - - [self setCrossFade:YES]; - [self setShiftSlowsAnimation:YES]; - } - return self; - - (void)window; // To prevent compiler warnings. -} - - - - -- (void)windowDidLoad -{ - // Create a new window to display the preference views. - // If the developer attached a window to this controller - // in Interface Builder, it gets replaced with this one. - NSWindow *window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,1000,1000) - styleMask:(NSTitledWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask) - backing:NSBackingStoreBuffered - defer:YES] autorelease]; - [self setWindow:window]; - contentSubview = [[[NSView alloc] initWithFrame:[[[self window] contentView] frame]] autorelease]; - [contentSubview setAutoresizingMask:(NSViewMinYMargin | NSViewWidthSizable)]; - [[[self window] contentView] addSubview:contentSubview]; - [[self window] setShowsToolbarButton:NO]; -} - - - - -- (void) dealloc { - [toolbarIdentifiers release]; - [toolbarViews release]; - [toolbarItems release]; - [viewAnimation release]; - [super dealloc]; -} - - - - -#pragma mark - -#pragma mark Configuration - - -- (void)setupToolbar -{ - // Subclasses must override this method to add items to the - // toolbar by calling -addView:label: or -addView:label:image:. -} - - - - -- (void)addView:(NSView *)view label:(NSString *)label -{ - [self addView:view - label:label - image:[NSImage imageNamed:label]]; -} - - - - -- (void)addView:(NSView *)view label:(NSString *)label image:(NSImage *)image -{ - NSAssert (view != nil, - @"Attempted to add a nil view when calling -addView:label:image:."); - - NSString *identifier = [[label copy] autorelease]; - - [toolbarIdentifiers addObject:identifier]; - [toolbarViews setObject:view forKey:identifier]; - - NSToolbarItem *item = [[[NSToolbarItem alloc] initWithItemIdentifier:identifier] autorelease]; - [item setLabel:label]; - [item setImage:image]; - [item setTarget:self]; - [item setAction:@selector(toggleActivePreferenceView:)]; - - [toolbarItems setObject:item forKey:identifier]; -} - - - - -#pragma mark - -#pragma mark Accessor Methods - - -- (BOOL)crossFade -{ - return _crossFade; -} - - - - -- (void)setCrossFade:(BOOL)fade -{ - _crossFade = fade; -} - - - - -- (BOOL)shiftSlowsAnimation -{ - return _shiftSlowsAnimation; -} - - - - -- (void)setShiftSlowsAnimation:(BOOL)slows -{ - _shiftSlowsAnimation = slows; -} - - - - -#pragma mark - -#pragma mark Overriding Methods - - -- (IBAction)showWindow:(id)sender -{ - // This forces the resources in the nib to load. - (void)[self window]; - - // Clear the last setup and get a fresh one. - [toolbarIdentifiers removeAllObjects]; - [toolbarViews removeAllObjects]; - [toolbarItems removeAllObjects]; - [self setupToolbar]; - - NSAssert (([toolbarIdentifiers count] > 0), - @"No items were added to the toolbar in -setupToolbar."); - - if ([[self window] toolbar] == nil) { - NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"DBPreferencesToolbar"]; - [toolbar setAllowsUserCustomization:NO]; - [toolbar setAutosavesConfiguration:NO]; - [toolbar setSizeMode:NSToolbarSizeModeDefault]; - [toolbar setDisplayMode:NSToolbarDisplayModeIconAndLabel]; - [toolbar setDelegate:self]; - [[self window] setToolbar:toolbar]; - [toolbar release]; - } - - NSString *firstIdentifier = [toolbarIdentifiers objectAtIndex:0]; - [[[self window] toolbar] setSelectedItemIdentifier:firstIdentifier]; - [self displayViewForIdentifier:firstIdentifier animate:NO]; - - [[self window] center]; - - [super showWindow:sender]; -} - - - - -#pragma mark - -#pragma mark Toolbar - - -- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar -{ - return toolbarIdentifiers; - - (void)toolbar; -} - - - - -- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar -{ - return toolbarIdentifiers; - - (void)toolbar; -} - - - - -- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar -{ - return toolbarIdentifiers; - (void)toolbar; -} - - - - -- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)identifier willBeInsertedIntoToolbar:(BOOL)willBeInserted -{ - return [toolbarItems objectForKey:identifier]; - (void)toolbar; - (void)willBeInserted; -} - - - - -- (void)toggleActivePreferenceView:(NSToolbarItem *)toolbarItem -{ - [self displayViewForIdentifier:[toolbarItem itemIdentifier] animate:YES]; -} - - - - -- (void)displayViewForIdentifier:(NSString *)identifier animate:(BOOL)animate -{ - // Find the view we want to display. - NSView *newView = [toolbarViews objectForKey:identifier]; - - // See if there are any visible views. - NSView *oldView = nil; - if ([[contentSubview subviews] count] > 0) { - // Get a list of all of the views in the window. Usually at this - // point there is just one visible view. But if the last fade - // hasn't finished, we need to get rid of it now before we move on. - NSEnumerator *subviewsEnum = [[contentSubview subviews] reverseObjectEnumerator]; - - // The first one (last one added) is our visible view. - oldView = [subviewsEnum nextObject]; - - // Remove any others. - NSView *reallyOldView = nil; - while ((reallyOldView = [subviewsEnum nextObject]) != nil) { - [reallyOldView removeFromSuperviewWithoutNeedingDisplay]; - } - } - - if (![newView isEqualTo:oldView]) { - NSRect frame = [newView bounds]; - frame.origin.y = NSHeight([contentSubview frame]) - NSHeight([newView bounds]); - [newView setFrame:frame]; - [contentSubview addSubview:newView]; - [[self window] setInitialFirstResponder:newView]; - - if (animate && [self crossFade]) - [self crossFadeView:oldView withView:newView]; - else { - [oldView removeFromSuperviewWithoutNeedingDisplay]; - [newView setHidden:NO]; - [[self window] setFrame:[self frameForView:newView] display:YES animate:animate]; - } - - [[self window] setTitle:[[toolbarItems objectForKey:identifier] label]]; - } -} - - - - -#pragma mark - -#pragma mark Cross-Fading Methods - - -- (void)crossFadeView:(NSView *)oldView withView:(NSView *)newView -{ - [viewAnimation stopAnimation]; - - if ([self shiftSlowsAnimation] && [[[self window] currentEvent] modifierFlags] & NSShiftKeyMask) - [viewAnimation setDuration:1.25]; - else - [viewAnimation setDuration:0.25]; - - NSDictionary *fadeOutDictionary = [NSDictionary dictionaryWithObjectsAndKeys: - oldView, NSViewAnimationTargetKey, - NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey, - nil]; - - NSDictionary *fadeInDictionary = [NSDictionary dictionaryWithObjectsAndKeys: - newView, NSViewAnimationTargetKey, - NSViewAnimationFadeInEffect, NSViewAnimationEffectKey, - nil]; - - NSDictionary *resizeDictionary = [NSDictionary dictionaryWithObjectsAndKeys: - [self window], NSViewAnimationTargetKey, - [NSValue valueWithRect:[[self window] frame]], NSViewAnimationStartFrameKey, - [NSValue valueWithRect:[self frameForView:newView]], NSViewAnimationEndFrameKey, - nil]; - - NSArray *animationArray = [NSArray arrayWithObjects: - fadeOutDictionary, - fadeInDictionary, - resizeDictionary, - nil]; - - [viewAnimation setViewAnimations:animationArray]; - [viewAnimation startAnimation]; -} - - - - -- (void)animationDidEnd:(NSAnimation *)animation -{ - NSView *subview; - - // Get a list of all of the views in the window. Hopefully - // at this point there are two. One is visible and one is hidden. - NSEnumerator *subviewsEnum = [[contentSubview subviews] reverseObjectEnumerator]; - - // This is our visible view. Just get past it. - subview = [subviewsEnum nextObject]; - - // Remove everything else. There should be just one, but - // if the user does a lot of fast clicking, we might have - // more than one to remove. - while ((subview = [subviewsEnum nextObject]) != nil) { - [subview removeFromSuperviewWithoutNeedingDisplay]; - } - - // This is a work-around that prevents the first - // toolbar icon from becoming highlighted. - [[self window] makeFirstResponder:nil]; - - (void)animation; -} - - - - -- (NSRect)frameForView:(NSView *)view - // Calculate the window size for the new view. -{ - NSRect windowFrame = [[self window] frame]; - NSRect contentRect = [[self window] contentRectForFrameRect:windowFrame]; - float windowTitleAndToolbarHeight = NSHeight(windowFrame) - NSHeight(contentRect); - - windowFrame.size.height = NSHeight([view frame]) + windowTitleAndToolbarHeight; - windowFrame.size.width = NSWidth([view frame]); - windowFrame.origin.y = NSMaxY([[self window] frame]) - NSHeight(windowFrame); - - return windowFrame; -} - - - - -@end diff --git a/Documentation/ReleaseNotes/v0.2.1.txt b/Documentation/ReleaseNotes/v0.2.1.txt deleted file mode 100644 index 8ed0e9fad..000000000 --- a/Documentation/ReleaseNotes/v0.2.1.txt +++ /dev/null @@ -1,3 +0,0 @@ -V0.2.1 -=== -* Added Sparkle update system diff --git a/Documentation/ReleaseNotes/v0.2.txt b/Documentation/ReleaseNotes/v0.2.txt deleted file mode 100644 index 29ab6a336..000000000 --- a/Documentation/ReleaseNotes/v0.2.txt +++ /dev/null @@ -1,4 +0,0 @@ -V0.2: Changes since v0.1 -==== -* The graph now has colors -* There are now lables attached to commits \ No newline at end of file diff --git a/Documentation/ReleaseNotes/v0.3.1.txt b/Documentation/ReleaseNotes/v0.3.1.txt deleted file mode 100644 index 05d8d00eb..000000000 --- a/Documentation/ReleaseNotes/v0.3.1.txt +++ /dev/null @@ -1,3 +0,0 @@ -v0.3.1 -=== -* Fixed a serious crasher diff --git a/Documentation/ReleaseNotes/v0.3.txt b/Documentation/ReleaseNotes/v0.3.txt deleted file mode 100644 index d46ea1d99..000000000 --- a/Documentation/ReleaseNotes/v0.3.txt +++ /dev/null @@ -1,10 +0,0 @@ -V0.3: Changes since v0.2.1 -==== -* You can now pass on command-line arguments just like you can with 'git log' -* The program has an icon -* Also displays remote branches in the branch list -* Is better in determining if a directory is a bare git repository -* Support for --left-right: use 'gitx --left-right HEAD..origin/master' - to see which commits are only on your branch or on their branch -* Navigate through changed hunks by using j/k keys -* Scroll down in webview by using space / shift-space diff --git a/Documentation/ReleaseNotes/v0.4.1.txt b/Documentation/ReleaseNotes/v0.4.1.txt deleted file mode 100644 index b8e6a93bc..000000000 --- a/Documentation/ReleaseNotes/v0.4.1.txt +++ /dev/null @@ -1,8 +0,0 @@ -v0.4.1 -== - -This is a maintenance release. Most important fixes: - -* The diff display is now much faster -* More locations are now searched for a default git -* Code pasted online is now private diff --git a/Documentation/ReleaseNotes/v0.4.txt b/Documentation/ReleaseNotes/v0.4.txt deleted file mode 100644 index 2a4efd01c..000000000 --- a/Documentation/ReleaseNotes/v0.4.txt +++ /dev/null @@ -1,11 +0,0 @@ -v0.4: Changes since v0.3.1 -=== -* A new commitview, allowing you to selectively add changes and commit them. -* You can now upload a commit as a patch to gist.github.com -* GitX now searches for your git binary in more directories and is smarter - about reporting errors regarding git paths. -* You can now remove branches by right-clicking on them in the detailed view -* GitX now comes with a spicy new icon -* The diff view has become prettier and now also highlights trailing - whitespace. -* Various little changes and stability improvement diff --git a/Documentation/ReleaseNotes/v0.5.txt b/Documentation/ReleaseNotes/v0.5.txt deleted file mode 100644 index 3cfcc8a17..000000000 --- a/Documentation/ReleaseNotes/v0.5.txt +++ /dev/null @@ -1,23 +0,0 @@ -v0.5 -==== - -This feature release has several new smaller or larger features: - -* The current branch is now highlighted -* In the commit view, there is an option to amend commits -* The "Gist it" button now respects github.user/token -* Display a gravatar of the committer -* The commit message view now displays a vertical line at 50 characters -* It is now possible to revert changes by using the context menu in the - commit view -* You can now stage only parts of a file by using the "Stage Hunk" buttons - in the commit view -* You can now use GitX to show a diff of anything, for example by using - 'gitx --diff HEAD^^' or 'git diff HEAD~3 | gitx --diff' -* You can now drag and drop refs to move them and also create branches - -In addition, the following bugs have been fixed: - -* Better detection of git version -* Branch lines are no longer interspersed with half a pixel of whitespace -* The toolbar keeps its state when switching views diff --git a/Documentation/ReleaseNotes/v0.6.1.txt b/Documentation/ReleaseNotes/v0.6.1.txt deleted file mode 100644 index c77a31bb9..000000000 --- a/Documentation/ReleaseNotes/v0.6.1.txt +++ /dev/null @@ -1,8 +0,0 @@ -v0.6.1 -=== - -This is a bugfix release. The following bugs have been fixed: - -* The commit view shows new files with linebreaks -* The history view works with Git >= 1.5.4 again -* Reloading the detailed view in the History no longer causes an empty page diff --git a/Documentation/ReleaseNotes/v0.6.2.txt b/Documentation/ReleaseNotes/v0.6.2.txt deleted file mode 100644 index 172b7991f..000000000 --- a/Documentation/ReleaseNotes/v0.6.2.txt +++ /dev/null @@ -1,19 +0,0 @@ -v0.6.2 -=== - -This is a maintenance release. The following bugs have been fixed: - -* Fix many display bugs in the history view (Thanks to Johannes Gilger) -* Fix moving of refs -* GitX no longer stalls if you have a large amount of untracked files -* GitX now asks for confirmation before deleting a branch -* GitX no longer shows ghost files after staging a hunk -* Dragging and dropping a tree now correctly copies all files - -In addition, the following features have been added: - -* There is now a 'New…' option to create a new repository -* The 'Gist It' feature now asks for confirmation by default. This can be changed in the preferences -* GitX can now open any directory by dragging it on the icon -* GitX now asks for confirmation before deleting a branch - diff --git a/Documentation/ReleaseNotes/v0.6.3.txt b/Documentation/ReleaseNotes/v0.6.3.txt deleted file mode 100644 index bed1fe847..000000000 --- a/Documentation/ReleaseNotes/v0.6.3.txt +++ /dev/null @@ -1,9 +0,0 @@ -v0.6.3 -=== - -This is a maintenance release. The following bugs have been fixed: - -* Fixed a problem where GitX could not open some directories, for example those that include a . [Johannes Gilger] -* Fixed a performance issue when staging or unstaging a lot of files -* Various buildfixes (Benjamin Kramer, Arjen Laarhoven) - diff --git a/Documentation/ReleaseNotes/v0.6.txt b/Documentation/ReleaseNotes/v0.6.txt deleted file mode 100644 index d152d89aa..000000000 --- a/Documentation/ReleaseNotes/v0.6.txt +++ /dev/null @@ -1,29 +0,0 @@ -v0.6 -=== - -This release has the following new features and enhancements: - -* The diff display now looks much nicer, using boxes to segment files -* The toolbar can now me customized -* Images that have been changed or added in a commit can now be viewed - inline in GitX -* GitX has gained a preference pane which allows you to specify a git path - and disable the Gist and Gravatar integration -* The commit interface is now more intuitive. Particularly, you can now - select multiple files and use drag and drop to stage / unstage files -* You can now drag and drop files out of the commit view -* The files in the commit view have gained a context menu that allows you - to revert changes / open the file / ignore the file -* It is now possible to adjust the amount of context lines in the commit view. - Using a smaller context size allows you to do more fine-grained commits -* The branch menu is now organized in branches/remotes/tags -* The view switch button now uses icons rather than words -* The view shortcuts have changed to use command 1/2 for the history/commit - view. The history's subviews can now be changed using command-option-1/2/3 -* Listing commits has become much faster -* GitX no longer spawns zombie processes -* GitX now shows a list of files that have been changed in a commit -* GitX now uses libgit2 to store object id's, reducing it's memory footprint - -In addition many bugs were fixed, including the correct calculation of a -gravatar MD5 hash. diff --git a/Documentation/ReleaseNotes/v0.7.txt b/Documentation/ReleaseNotes/v0.7.txt deleted file mode 100644 index 35da971f9..000000000 --- a/Documentation/ReleaseNotes/v0.7.txt +++ /dev/null @@ -1,35 +0,0 @@ -v0.7 -=== - -As a GitX user, we would like you to help us focus our development by telling -us your wishes. You can help us by filling in this - [survey](http://www.survs.com/survey?id=DCJKLP2B&channel=UY6RRC3MXZ "GitX User survey")! - -This feature-release has the following new features and enhancements: - -* Line-wise staging by selecting lines from hunks (JD Smith) -* Discarding hunks/changes to files (using git apply --reverse) (Johannes Gilger) -* Show current HEAD in window-title (Johannes Gilger) -* Show current branch in orange in history view (Steven Michalske) -* Collapse commit-list/diff-view using Command-Shift-Up/Down (Johannes Gilger) -* Sign-Off commits in commit view (Johannes Gilger, Nicolas Riley) -* "Show in Finder" for files in commit view (Charles O'Rourke) -* Commit hooks are executed when committing (Joe Fiorini, Pieter de Bie) -* Branches can be deleted from the UI - -Small enhancements: - -* Option to disable the "Open" dialog on start (Stonewall Ballard) -* Show committer name if it differs from authors name (Benjamin Kramer) -* Columns in history view can be toggled (Pieter de Bie) -* Reuse author-information when amending a commit (Pieter de Bie) -* Commit interface is now faster (Pieter de Bie) -* Use modal alerts where possible -* Don't display content of binary-files in tree-view -* The user manual has been updated for 0.7 (Johannes Gilger) -* Make the Commit UI a bit more informative (Nicholas Riley) - - -In addition a lot of bugs were fixed and numerous tiny features introduced. - -Credits go to: Pieter de Bie, Johannes Gilger, Benjamin Kramer, Nicholas Riley, JD Smith, Joe Fiorini, Dave Grijalva, Charles O'Rourke, Gerd Knops, dbr, Mike Czepiel, Benoit Cerrina, Steven Michalske and Stonewall Ballard diff --git a/Documentation/Screenshot.png b/Documentation/Screenshot.png new file mode 100644 index 000000000..4d7b7eed3 Binary files /dev/null and b/Documentation/Screenshot.png differ diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings deleted file mode 100644 index 935a55311..000000000 Binary files a/English.lproj/InfoPlist.strings and /dev/null differ diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib deleted file mode 100644 index 8dbbc7494..000000000 --- a/English.lproj/MainMenu.xib +++ /dev/null @@ -1,2561 +0,0 @@ - - - - 1050 - 9G55 - 677 - 949.43 - 353.00 - - YES - - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - - NSApplication - - - - FirstResponder - - - NSApplication - - - MainMenu - - YES - - - GitX - - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - GitX - - YES - - - About GitX - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - RW5hYmxlIFRlcm1pbmFsIFVzYWdl4oCmA - - 1048576 - 2147483647 - - - - - - UHJlZmVyZW5jZXPigKY - , - 1048576 - 2147483647 - - - - - - Q2hlY2sgZm9yIFVwZGF0ZXPigKY - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Services - - 1048576 - 2147483647 - - - submenuAction: - - - Services - - - YES - - _NSServicesMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Hide GitX - h - 1048576 - 2147483647 - - - - - - Hide Others - h - 1572864 - 2147483647 - - - - - - Show All - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Quit GitX - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - - File - - 1048576 - 2147483647 - - - submenuAction: - - - File - - - YES - - - TmV34oCmA - n - 1048576 - 2147483647 - - - - - - T3BlbuKApg - o - 1048576 - 2147483647 - - - - - - Open Recent - - 1048576 - 2147483647 - - - submenuAction: - - - Open Recent - - - YES - - - Clear Menu - - 1048576 - 2147483647 - - - - - _NSRecentDocumentsMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Close - w - 1048576 - 2147483647 - - - - - - Save - s - 1048576 - 2147483647 - - - - - - U2F2ZSBBc+KApg - S - 1048576 - 2147483647 - - - - - - Revert to Saved - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - UGFnZSBTZXR1cOKApg - P - 1048576 - 2147483647 - - - - - - UHJpbnTigKY - p - 1048576 - 2147483647 - - - - - - - - - Edit - - 1048576 - 2147483647 - - - submenuAction: - - - Edit - - - YES - - - Undo - z - 1048576 - 2147483647 - - - - - - Redo - Z - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Cut - x - 1048576 - 2147483647 - - - - - - Copy - c - 1048576 - 2147483647 - - - - - - Paste - v - 1048576 - 2147483647 - - - - - - Delete - - 1048576 - 2147483647 - - - - - - Select All - a - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Find - - 1048576 - 2147483647 - - - submenuAction: - - - Find - - - YES - - - RmluZOKApg - f - 1048576 - 2147483647 - - - 1 - - - - Find Next - g - 1048576 - 2147483647 - - - 2 - - - - Find Previous - G - 1048576 - 2147483647 - - - 3 - - - - Use Selection for Find - e - 1048576 - 2147483647 - - - 7 - - - - Jump to Selection - j - 1048576 - 2147483647 - - - - - - - - - Spelling and Grammar - - 1048576 - 2147483647 - - - submenuAction: - - Spelling and Grammar - - YES - - - U2hvdyBTcGVsbGluZ+KApg - : - 1048576 - 2147483647 - - - - - - Check Spelling - ; - 1048576 - 2147483647 - - - - - - Check Spelling While Typing - - 1048576 - 2147483647 - - - - - - Check Grammar With Spelling - - 1048576 - 2147483647 - - - - - - - - - Substitutions - - 1048576 - 2147483647 - - - submenuAction: - - Substitutions - - YES - - - Smart Copy/Paste - f - 1048576 - 2147483647 - - - - - - Smart Quotes - g - 1048576 - 2147483647 - - - - - - Smart Links - G - 1048576 - 2147483647 - - - - - - - - - Speech - - 1048576 - 2147483647 - - - submenuAction: - - Speech - - YES - - - Start Speaking - - 2147483647 - - - - - - Stop Speaking - - 2147483647 - - - - - - - - - - - - View - - 1048576 - 2147483647 - - - submenuAction: - - View - - YES - - - Show Toolbar - t - 1572864 - 2147483647 - - - - - - Q3VzdG9taXplIFRvb2xiYXLigKY - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - History - 1 - 1048576 - 2147483647 - - - - - - Commit - 2 - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Detailed View - 1 - 1572864 - 2147483647 - - - - - - Raw View - 2 - 1572864 - 2147483647 - - - - - - Tree View - 3 - 1572864 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - Refresh - r - 1048576 - 2147483647 - - - - - - - - - Window - - 1048576 - 2147483647 - - - submenuAction: - - - Window - - - YES - - - Minimize - m - 1048576 - 2147483647 - - - - - - Zoom - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Bring All to Front - - 1048576 - 2147483647 - - - - - _NSWindowsMenu - - - - - Help - - 1048576 - 2147483647 - - - submenuAction: - - Help - - YES - - - GitX Help - ? - 1048576 - 2147483647 - - - - - - - - _NSMainMenu - - - ApplicationController - - - PBRepositoryDocumentController - - - YES - - - SUUpdater - - - - - YES - - - print: - - - - 86 - - - - runPageLayout: - - - - 87 - - - - clearRecentDocuments: - - - - 127 - - - - terminate: - - - - 139 - - - - showAboutPanel: - - - - 142 - - - - hideOtherApplications: - - - - 146 - - - - hide: - - - - 152 - - - - unhideAllApplications: - - - - 153 - - - - cut: - - - - 175 - - - - paste: - - - - 176 - - - - redo: - - - - 178 - - - - selectAll: - - - - 179 - - - - undo: - - - - 180 - - - - copy: - - - - 181 - - - - showGuessPanel: - - - - 188 - - - - checkSpelling: - - - - 190 - - - - toggleContinuousSpellChecking: - - - - 192 - - - - performClose: - - - - 193 - - - - delete: - - - - 195 - - - - performFindPanelAction: - - - - 199 - - - - performFindPanelAction: - - - - 200 - - - - performFindPanelAction: - - - - 201 - - - - performFindPanelAction: - - - - 202 - - - - centerSelectionInVisibleArea: - - - - 203 - - - - delegate - - - - 206 - - - - saveAction: - - - - 211 - - - - performMiniaturize: - - - - 247 - - - - performZoom: - - - - 248 - - - - arrangeInFront: - - - - 249 - - - - startSpeaking: - - - - 257 - - - - stopSpeaking: - - - - 258 - - - - toggleToolbarShown: - - - - 342 - - - - runToolbarCustomizationPalette: - - - - 343 - - - - firstResponder - - - - 868 - - - - openDocument: - - - - 907 - - - - installCliTool: - - - - 910 - - - - checkForUpdates: - - - - 920 - - - - setDetailedView: - - - - 923 - - - - setRawView: - - - - 924 - - - - setTreeView: - - - - 925 - - - - refresh: - - - - 926 - - - - showHistoryView: - - - - 930 - - - - showCommitView: - - - - 931 - - - - showHelp: - - - - 932 - - - - openPreferencesWindow: - - - - 933 - - - - newDocument: - - - - 934 - - - - - YES - - 0 - - YES - - - - - - -2 - - - RmlsZSdzIE93bmVyA - - - -1 - - - First Responder - - - -3 - - - Application - - - 29 - - - YES - - - - - - - - - MainMenu - - - 56 - - - YES - - - - - - 57 - - - YES - - - - - - - - - - - - - - - - - - 58 - - - - - 129 - - - - - 131 - - - YES - - - - - - 130 - - - - - 134 - - - - - 136 - - - - - 143 - - - - - 144 - - - - - 145 - - - - - 149 - - - - - 150 - - - - - 196 - - - - - 83 - - - YES - - - - - - 81 - - - YES - - - - - - - - - - - - - - - - 72 - - - - - 73 - - - - - 74 - - - - - 75 - - - - - 77 - - - - - 78 - - - - - 79 - - - - - 80 - - - - - 82 - - - - - 112 - - - - - 124 - - - YES - - - - - - 125 - - - YES - - - - - - 126 - - - - - 103 - - - YES - - - - - - 106 - - - YES - - - - - - 111 - - - - - 163 - - - YES - - - - - - 169 - - - YES - - - - - - - - - - - - - - - - - - 156 - - - - - 157 - - - - - 158 - - - - - 160 - - - - - 164 - - - - - 168 - - - YES - - - - - - 159 - - - YES - - - - - - - - - - 154 - - - - - 155 - - - - - 161 - - - - - 162 - - - - - 167 - - - - - 171 - - - - - 172 - - - - - 173 - - - - - 174 - - - - - 184 - - - YES - - - - - - 185 - - - YES - - - - - - - - - 187 - - - - - 189 - - - - - 191 - - - - - 212 - - - - - 214 - - - YES - - - - - - 215 - - - YES - - - - - - - - 216 - - - - - 218 - - - - - 219 - - - - - 224 - - - YES - - - - - - 225 - - - YES - - - - - - - 227 - - - - - 228 - - - - - 241 - - - YES - - - - - - 242 - - - YES - - - - - - - - - 243 - - - - - 244 - - - - - 245 - - - - - 246 - - - - - 338 - - - YES - - - - - - 339 - - - YES - - - - - - - - - - - - - - - - 340 - - - - - 341 - - - - - 205 - - - ApplicationController - - - 847 - - - - - 848 - - - - - 851 - - - - - 852 - - - - - 908 - - - - - 909 - - - - - 912 - - - - - 915 - - - - - 916 - - - - - 918 - - - - - 919 - - - - - 927 - - - - - 928 - - - - - 929 - - - - - - - YES - - YES - -1.IBPluginDependency - -2.IBPluginDependency - -3.IBPluginDependency - -3.ImportedFromIB2 - 103.IBPluginDependency - 103.ImportedFromIB2 - 106.IBEditorWindowLastContentRect - 106.IBPluginDependency - 106.ImportedFromIB2 - 111.IBPluginDependency - 111.ImportedFromIB2 - 112.IBPluginDependency - 112.ImportedFromIB2 - 124.IBPluginDependency - 124.ImportedFromIB2 - 125.IBPluginDependency - 125.ImportedFromIB2 - 126.IBPluginDependency - 126.ImportedFromIB2 - 129.IBPluginDependency - 129.ImportedFromIB2 - 130.IBPluginDependency - 130.ImportedFromIB2 - 131.IBPluginDependency - 131.ImportedFromIB2 - 134.IBPluginDependency - 134.ImportedFromIB2 - 136.IBPluginDependency - 136.ImportedFromIB2 - 143.IBPluginDependency - 143.ImportedFromIB2 - 144.IBPluginDependency - 144.ImportedFromIB2 - 145.IBPluginDependency - 145.ImportedFromIB2 - 149.IBPluginDependency - 149.ImportedFromIB2 - 150.IBPluginDependency - 150.ImportedFromIB2 - 154.IBPluginDependency - 154.ImportedFromIB2 - 155.IBPluginDependency - 155.ImportedFromIB2 - 156.IBPluginDependency - 156.ImportedFromIB2 - 157.IBPluginDependency - 157.ImportedFromIB2 - 158.IBPluginDependency - 158.ImportedFromIB2 - 159.IBPluginDependency - 159.ImportedFromIB2 - 160.IBPluginDependency - 160.ImportedFromIB2 - 161.IBPluginDependency - 161.ImportedFromIB2 - 162.IBPluginDependency - 162.ImportedFromIB2 - 163.IBPluginDependency - 163.ImportedFromIB2 - 164.IBPluginDependency - 164.ImportedFromIB2 - 167.IBPluginDependency - 167.ImportedFromIB2 - 168.IBPluginDependency - 168.ImportedFromIB2 - 169.IBEditorWindowLastContentRect - 169.IBPluginDependency - 169.ImportedFromIB2 - 169.editorWindowContentRectSynchronizationRect - 171.IBPluginDependency - 171.ImportedFromIB2 - 172.IBPluginDependency - 172.ImportedFromIB2 - 173.IBPluginDependency - 173.ImportedFromIB2 - 174.IBPluginDependency - 174.ImportedFromIB2 - 184.IBPluginDependency - 184.ImportedFromIB2 - 185.IBPluginDependency - 185.ImportedFromIB2 - 187.IBPluginDependency - 187.ImportedFromIB2 - 189.IBPluginDependency - 189.ImportedFromIB2 - 191.IBPluginDependency - 191.ImportedFromIB2 - 196.IBPluginDependency - 196.ImportedFromIB2 - 205.IBPluginDependency - 205.ImportedFromIB2 - 212.IBPluginDependency - 212.ImportedFromIB2 - 214.IBPluginDependency - 214.ImportedFromIB2 - 215.IBPluginDependency - 215.ImportedFromIB2 - 216.IBPluginDependency - 216.ImportedFromIB2 - 218.IBPluginDependency - 218.ImportedFromIB2 - 219.IBPluginDependency - 219.ImportedFromIB2 - 224.IBPluginDependency - 224.ImportedFromIB2 - 225.IBPluginDependency - 225.ImportedFromIB2 - 227.IBPluginDependency - 227.ImportedFromIB2 - 228.IBPluginDependency - 228.ImportedFromIB2 - 241.IBPluginDependency - 241.ImportedFromIB2 - 242.IBEditorWindowLastContentRect - 242.IBPluginDependency - 242.ImportedFromIB2 - 243.IBPluginDependency - 243.ImportedFromIB2 - 244.IBPluginDependency - 244.ImportedFromIB2 - 245.IBPluginDependency - 245.ImportedFromIB2 - 246.IBPluginDependency - 246.ImportedFromIB2 - 29.IBEditorWindowLastContentRect - 29.IBPluginDependency - 29.ImportedFromIB2 - 29.editorWindowContentRectSynchronizationRect - 338.IBPluginDependency - 338.ImportedFromIB2 - 339.IBEditorWindowLastContentRect - 339.IBPluginDependency - 339.ImportedFromIB2 - 339.editorWindowContentRectSynchronizationRect - 340.IBPluginDependency - 340.ImportedFromIB2 - 341.IBPluginDependency - 341.ImportedFromIB2 - 56.IBPluginDependency - 56.ImportedFromIB2 - 57.IBEditorWindowLastContentRect - 57.IBPluginDependency - 57.ImportedFromIB2 - 57.editorWindowContentRectSynchronizationRect - 58.IBPluginDependency - 58.ImportedFromIB2 - 72.IBPluginDependency - 72.ImportedFromIB2 - 73.IBPluginDependency - 73.ImportedFromIB2 - 74.IBPluginDependency - 74.ImportedFromIB2 - 75.IBPluginDependency - 75.ImportedFromIB2 - 77.IBPluginDependency - 77.ImportedFromIB2 - 78.IBPluginDependency - 78.ImportedFromIB2 - 79.IBPluginDependency - 79.ImportedFromIB2 - 80.IBPluginDependency - 80.ImportedFromIB2 - 81.IBEditorWindowLastContentRect - 81.IBPluginDependency - 81.ImportedFromIB2 - 81.editorWindowContentRectSynchronizationRect - 82.IBPluginDependency - 82.ImportedFromIB2 - 83.IBPluginDependency - 83.ImportedFromIB2 - 847.IBPluginDependency - 848.IBPluginDependency - 851.IBPluginDependency - 852.IBPluginDependency - 908.IBPluginDependency - 909.IBPluginDependency - 912.IBPluginDependency - 915.IBPluginDependency - 916.IBPluginDependency - 918.IBPluginDependency - 919.IBPluginDependency - 927.IBPluginDependency - 928.IBPluginDependency - 929.IBPluginDependency - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{789, 713}, {138, 23}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{370, 623}, {243, 243}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{455, 493}, {243, 243}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{718, 767}, {197, 73}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{521, 736}, {329, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{297, 739}, {329, 20}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{668, 647}, {234, 193}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{499, 623}, {234, 113}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{533, 513}, {262, 223}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{309, 536}, {262, 203}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - {{582, 533}, {199, 203}} - com.apple.InterfaceBuilder.CocoaPlugin - - {{358, 536}, {199, 203}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 934 - - - - YES - - ApplicationController - NSObject - - YES - - YES - installCliTool: - openPreferencesWindow: - showAboutPanel: - saveAction: - showHelp: - - - YES - id - id - id - id - id - - - - YES - - YES - firstResponder - window - - - YES - id - NSWindow - - - - IBProjectSource - ApplicationController.h - - - - ApplicationController - NSObject - - IBUserSource - - - - - FirstResponder - NSObject - - YES - - YES - setDetailedView: - setRawView: - setTreeView: - - - YES - id - id - id - - - - IBUserSource - - - - - PBGitCommitController - PBViewController - - YES - - YES - commit: - refresh: - - - YES - id - id - - - - YES - - YES - cachedFilesController - commitMessageView - indexController - unstagedFilesController - webController - - - YES - NSArrayController - NSTextView - id - NSArrayController - id - - - - IBProjectSource - PBGitCommitController.h - - - - PBGitHistoryController - PBViewController - - YES - - YES - openSelectedFile: - refresh: - setDetailedView: - setRawView: - setTreeView: - toggleQuickView: - - - YES - id - id - id - id - id - id - - - - YES - - YES - commitController - commitList - fileBrowser - searchField - treeController - webView - - - YES - NSArrayController - NSTableView - NSOutlineView - NSSearchField - NSTreeController - id - - - - IBProjectSource - PBGitHistoryController.h - - - - PBGitWindowController - NSWindowController - - YES - - YES - showCommitView: - showHistoryView: - - - YES - id - id - - - - contentView - NSView - - - IBProjectSource - PBGitWindowController.h - - - - PBGitWindowController - NSWindowController - - YES - - YES - openSelectedFile: - refresh: - setDetailedView: - setRawView: - setTreeView: - toggleQuickView: - - - YES - id - id - id - id - id - id - - - - YES - - YES - commitController - commitList - fileBrowser - treeController - - - YES - NSArrayController - NSTableView - NSOutlineView - NSTreeController - - - - IBUserSource - - - - - PBRepositoryDocumentController - NSDocumentController - - IBProjectSource - PBRepositoryDocumentController.h - - - - PBRepositoryDocumentController - NSDocumentController - - IBUserSource - - - - - PBViewController - NSViewController - - viewToolbar - NSToolbar - - - IBProjectSource - PBViewController.h - - - - - YES - - NSObject - - IBDocumentRelativeSource - ../Sparkle.framework/Versions/A/Headers/SUUpdater.h - - - - SUUpdater - NSObject - - checkForUpdates: - id - - - delegate - id - - - - - - 0 - ../GitX.xcodeproj - 3 - - diff --git a/English.lproj/Preferences.xib b/English.lproj/Preferences.xib deleted file mode 100644 index 465f1e16f..000000000 --- a/English.lproj/Preferences.xib +++ /dev/null @@ -1,1534 +0,0 @@ - - - - 1050 - 9J61 - 677 - 949.46 - 353.00 - - YES - - - - - - - YES - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - PBPrefsWindowController - - - FirstResponder - - - NSApplication - - - - 268 - - YES - - - 268 - {{17, 54}, {99, 17}} - - YES - - 68288064 - 272630784 - Git Executable: - - LucidaGrande - 1.300000e+01 - 1044 - - - - 6 - System - controlColor - - 3 - MC42NjY2NjY2OQA - - - - 6 - System - controlTextColor - - 3 - MAA - - - - - - - 268 - - YES - - YES - Apple URL pasteboard type - NSFilenamesPboardType - - - {{121, 50}, {179, 22}} - - YES - - 337772033 - 163840 - - LucidaGrande - 1.100000e+01 - 3100 - - - - YES - - 2 - - - - - - 268 - {{118, 0}, {192, 42}} - - YES - - 67239424 - 272760832 - Select the git executable you wish to use or drag it from the finder. - - - - - - - - - 268 - {{306, 54}, {54, 14}} - - YES - - -2080244224 - 0 - - - - 1211912447 - 130 - - NSImage - NSStopProgressFreestandingTemplate - - - - - 200 - 25 - - - - - 268 - {{18, 103}, {203, 18}} - - YES - - -2080244224 - 0 - Show whitespace differences - - - 1211912703 - 2 - - NSImage - NSSwitch - - - NSSwitch - - - - 200 - 25 - - - - - 268 - {{18, 78}, {207, 18}} - - YES - - -2080244224 - 0 - U2hvdyAiT3BlbiIgcGFuZWwgb24gbGF1bmNoA - - - 1211912703 - 2 - - - - - 200 - 25 - - - - {400, 139} - - NSView - - - - 268 - - YES - - - 268 - {{39, 45}, {82, 14}} - - YES - - 68288064 - 272761856 - Last update: - - - - - - - - - 268 - {{18, 103}, {260, 18}} - - YES - - -2080244224 - 0 - Automatically check for updates - - - 1211912703 - 2 - - - - - 200 - 25 - - - - - 268 - {{130, 78}, {100, 22}} - - YES - - -2076049856 - 133120 - - - 109199615 - 129 - - - 400 - 75 - - - Monthly - - 1048576 - 2147483647 - 1 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - _popUpItemAction: - 2629800 - - - YES - - OtherViews - - YES - - - Hourly - - 1048576 - 2147483647 - - - _popUpItemAction: - 3600 - - - - - Daily - - 1048576 - 2147483647 - - - _popUpItemAction: - 86400 - - - - - Weekly - - 1048576 - 2147483647 - - - _popUpItemAction: - 604800 - - - - - - 3 - 1 - YES - YES - 2 - - - - - 268 - {{39, 80}, {89, 17}} - - YES - - 68288064 - 272761856 - Update interval: - - - - - - - - - 268 - {{130, 45}, {251, 14}} - - YES - - 68288064 - -1874721792 - DATE ... - - - - YES - - YES - dateFormat_10_0 - dateStyle - formatterBehavior - timeStyle - - - YES - %m/%d/%y - - - - - - EEEE, MMMM d, yyyy h:mm:ss a - NO - - - - - - - - - 268 - {{128, 13}, {96, 28}} - - YES - - 67239424 - 134348800 - Check Now - - - -2038284033 - 129 - - - 200 - 25 - - - - {400, 139} - - NSView - - - - 268 - - YES - - - 268 - {{18, 18}, {203, 18}} - - YES - - 67239424 - 0 - Show all files and directories - - - 1211912703 - 2 - - - - - 200 - 25 - - - - {239, 54} - - NSView - - - SUUpdater - - - - YES - PBShowOpenPanelOnLaunch - - YES - - - - 268 - - YES - - - 268 - {{18, 80}, {111, 18}} - - YES - - -2080244224 - 0 - RW5hYmxlICdHaXN0IGl0Jw - - - 1211912703 - 2 - - - - - 200 - 25 - - - - - 268 - {{18, 18}, {121, 18}} - - YES - - -2080244224 - 0 - Enable Gravatar - - - 1211912703 - 2 - - - - - 200 - 25 - - - - - 268 - {{38, 60}, {181, 18}} - - YES - - -2080244224 - 0 - Confirm creation of Gists - - - 1211912703 - 2 - - - - - 200 - 25 - - - - - 268 - {{38, 38}, {179, 18}} - - YES - - -2080244224 - 0 - Make Gists public - - - 1211912703 - 2 - - - - - 200 - 25 - - - - {400, 116} - - NSView - - - - - YES - - - updatesPrefsView - - - - 7 - - - - generalPrefsView - - - - 8 - - - - value: automaticallyChecksForUpdates - - - - - - value: automaticallyChecksForUpdates - value - automaticallyChecksForUpdates - 2 - - - 27 - - - - selectedTag: updateCheckInterval - - - - - - selectedTag: updateCheckInterval - selectedTag - updateCheckInterval - 2 - - - 31 - - - - enabled: automaticallyChecksForUpdates - - - - - - enabled: automaticallyChecksForUpdates - enabled - automaticallyChecksForUpdates - 2 - - - 33 - - - - enabled: automaticallyChecksForUpdates - - - - - - enabled: automaticallyChecksForUpdates - enabled - automaticallyChecksForUpdates - 2 - - - 36 - - - - value: values.SULastCheckTime - - - - - - value: values.SULastCheckTime - value - values.SULastCheckTime - 2 - - - 41 - - - - delegate - - - - 54 - - - - checkForUpdates: - - - - 55 - - - - checkGitValidity: - - - - 58 - - - - gitPathController - - - - 59 - - - - delegate - - - - 61 - - - - gitPathOpenAccessory - - - - 65 - - - - showHideAllFiles: - - - - 66 - - - - resetGitPath: - - - - 79 - - - - hidden: values.gitExecutable - - - - - - hidden: values.gitExecutable - hidden - values.gitExecutable - - NSValueTransformerName - NSIsNil - - 2 - - - 83 - - - - value: values.gitExecutable - - - - - - value: values.gitExecutable - value - values.gitExecutable - - YES - - YES - NSAllowsEditingMultipleValuesSelection - NSNullPlaceholder - NSValueTransformerName - - - YES - - No Executable set - PBNSURLPathUserDefaultsTransfomer - - - 2 - - - 86 - - - - integrationPrefsView - - - - 92 - - - - value: values.PBEnableGist - - - - - - value: values.PBEnableGist - value - values.PBEnableGist - 2 - - - 94 - - - - value: values.PBEnableGravatar - - - - - - value: values.PBEnableGravatar - value - values.PBEnableGravatar - 2 - - - 96 - - - - value: values.PBConfirmPublicGists - - - - - - value: values.PBConfirmPublicGists - value - values.PBConfirmPublicGists - 2 - - - 104 - - - - enabled: values.PBEnableGist - - - - - - enabled: values.PBEnableGist - enabled - values.PBEnableGist - 2 - - - 107 - - - - enabled: values.PBEnableGist - - - - - - enabled: values.PBEnableGist - enabled - values.PBEnableGist - 2 - - - 111 - - - - value: values.PBGistPublic - - - - - - value: values.PBGistPublic - value - values.PBGistPublic - 2 - - - 113 - - - - value: values.PBShowWhitespaceDifferences - - - - - - value: values.PBShowWhitespaceDifferences - value - values.PBShowWhitespaceDifferences - 2 - - - 117 - - - - value: values.PBShowOpenPanelOnLaunch - - - - - - value: values.PBShowOpenPanelOnLaunch - value - values.PBShowOpenPanelOnLaunch - 2 - - - 121 - - - - - YES - - 0 - - YES - - - - - - -2 - - - RmlsZSdzIE93bmVyA - - - -1 - - - First Responder - - - -3 - - - Application - - - 1 - - - YES - - - - - - - - - General - - - 4 - - - YES - - - - - - - - - Updates - - - 9 - - - YES - - - - - - 10 - - - - - 11 - - - YES - - - - - - 12 - - - - - 13 - - - YES - - - - - - 14 - - - YES - - - - - - 15 - - - YES - - - - - - - - - 16 - - - - - 17 - - - - - 18 - - - - - 19 - - - YES - - - - - - 20 - - - - - 21 - - - YES - - - - - - 22 - - - YES - - - - - - 23 - - - YES - - - - - - 24 - - - - - 25 - - - - - 26 - - - - - 28 - - - - - 42 - - - - - 43 - - - YES - - - - - - 44 - - - - - 45 - - - YES - - - - - - 46 - - - - - 47 - - - YES - - - - - - 48 - - - - - 62 - - - YES - - - - Open Panel Accessory - - - 63 - - - YES - - - - - - 64 - - - - - 77 - - - YES - - - - - - 78 - - - - - 87 - - - YES - - - - - - - Integration - - - 88 - - - YES - - - - - - 89 - - - YES - - - - - - 90 - - - - - 91 - - - - - 97 - - - YES - - - - - - 98 - - - - - 108 - - - YES - - - - - - 109 - - - - - 114 - - - YES - - - - - - 115 - - - - - 118 - - - YES - - - - - - 119 - - - - - - - YES - - YES - -1.IBPluginDependency - -2.IBPluginDependency - -3.IBPluginDependency - 1.IBEditorWindowLastContentRect - 1.IBPluginDependency - 1.IBUserGuides - 1.WindowOrigin - 1.editorWindowContentRectSynchronizationRect - 10.IBPluginDependency - 108.IBPluginDependency - 109.IBPluginDependency - 11.IBPluginDependency - 114.IBPluginDependency - 115.IBPluginDependency - 118.IBPluginDependency - 119.IBPluginDependency - 12.IBPluginDependency - 13.IBPluginDependency - 14.IBPluginDependency - 15.IBEditorWindowLastContentRect - 15.IBPluginDependency - 16.IBPluginDependency - 17.IBPluginDependency - 18.IBPluginDependency - 19.IBPluginDependency - 20.IBPluginDependency - 21.IBPluginDependency - 22.IBPluginDependency - 23.IBPluginDependency - 24.IBPluginDependency - 25.IBPluginDependency - 26.IBPluginDependency - 4.IBEditorWindowLastContentRect - 4.IBPluginDependency - 4.IBUserGuides - 42.IBPluginDependency - 43.IBPluginDependency - 44.IBPluginDependency - 45.IBPluginDependency - 46.IBPluginDependency - 47.IBPluginDependency - 48.IBPluginDependency - 62.IBEditorWindowLastContentRect - 62.IBPluginDependency - 63.IBPluginDependency - 64.IBPluginDependency - 77.IBPluginDependency - 78.IBPluginDependency - 87.IBEditorWindowLastContentRect - 87.IBPluginDependency - 88.IBPluginDependency - 89.IBPluginDependency - 9.IBPluginDependency - 90.IBPluginDependency - 91.IBPluginDependency - 97.IBPluginDependency - 98.IBPluginDependency - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilderKit - {{477, 551}, {400, 139}} - com.apple.InterfaceBuilder.CocoaPlugin - - YES - - - 1.210000e+02 - 0 - - - {628, 654} - {{217, 442}, {480, 272}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{514, 459}, {106, 71}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{324, 683}, {400, 139}} - com.apple.InterfaceBuilder.CocoaPlugin - - YES - - - 4.200000e+01 - 0 - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{474, 394}, {239, 54}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{474, 352}, {400, 116}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 121 - - - - YES - - DBPrefsWindowController - NSWindowController - - IBProjectSource - DBPrefsWindowController.h - - - - PBPrefsWindowController - DBPrefsWindowController - - YES - - YES - checkGitValidity: - resetGitPath: - showHideAllFiles: - - - YES - id - id - id - - - - YES - - YES - badGitPathIcon - generalPrefsView - gitPathController - gitPathOpenAccessory - integrationPrefsView - updatesPrefsView - - - YES - NSImageView - NSView - NSPathControl - NSView - NSView - NSView - - - - IBProjectSource - PBPrefsWindowController.h - - - - - YES - - NSObject - - IBDocumentRelativeSource - ../Sparkle.framework/Versions/A/Headers/SUAppcast.h - - - - NSObject - - IBDocumentRelativeSource - ../Sparkle.framework/Versions/A/Headers/SUUpdater.h - - - - SUUpdater - NSObject - - checkForUpdates: - id - - - delegate - id - - - - - - 0 - ../GitX.xcodeproj - 3 - - diff --git a/English.lproj/RepositoryWindow.xib b/English.lproj/RepositoryWindow.xib deleted file mode 100644 index 003254bda..000000000 --- a/English.lproj/RepositoryWindow.xib +++ /dev/null @@ -1,230 +0,0 @@ - - - - 1050 - 9F33 - 677 - 949.34 - 352.00 - - YES - - - - YES - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - PBGitWindowController - - - FirstResponder - - - NSApplication - - - 15 - 2 - {{4, 386}, {850, 418}} - 1886912512 - GitX - NSWindow - - {3.40282e+38, 3.40282e+38} - {213, 107} - - - 274 - {850, 418} - - - {{0, 0}, {1440, 878}} - {213, 129} - {3.40282e+38, 3.40282e+38} - GitX - - - - - YES - - - contentView - - - - 245 - - - - window - - - - 292 - - - - - YES - - 0 - - YES - - - - - - -2 - - - RmlsZSdzIE93bmVyA - - - -1 - - - First Responder - - - -3 - - - Application - - - 3 - - - YES - - - - Window - - - 5 - - - YES - - - - - - - YES - - YES - -1.IBPluginDependency - -2.IBPluginDependency - -3.IBPluginDependency - 3.IBEditorWindowLastContentRect - 3.IBWindowTemplateEditedContentRect - 3.ImportedFromIB2 - 3.NSWindowTemplate.visibleAtLaunch - 3.editorWindowContentRectSynchronizationRect - 3.windowTemplate.hasMaxSize - 3.windowTemplate.hasMinSize - 3.windowTemplate.maxSize - 3.windowTemplate.minSize - 5.IBPluginDependency - 5.ImportedFromIB2 - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilderKit - {{1047, 395}, {850, 418}} - {{1047, 395}, {850, 418}} - - - {{15, 196}, {850, 418}} - - - {3.40282e+38, 3.40282e+38} - {213, 107} - com.apple.InterfaceBuilder.CocoaPlugin - - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 350 - - - - YES - - PBGitWindowController - NSWindowController - - YES - - YES - showCommitView: - showHistoryView: - - - YES - id - id - - - - contentView - NSView - - - IBProjectSource - PBGitWindowController.h - - - - PBGitWindowController - NSWindowController - - IBUserSource - - - - - - 0 - ../GitX.xcodeproj - 3 - - diff --git a/External/SyntaxHighlighter b/External/SyntaxHighlighter new file mode 160000 index 000000000..e2e50178b --- /dev/null +++ b/External/SyntaxHighlighter @@ -0,0 +1 @@ +Subproject commit e2e50178bff9330c6a91d3f0c7bd70dd8837ff94 diff --git a/GitTest_DataModel.xcdatamodel/elements b/GitTest_DataModel.xcdatamodel/elements deleted file mode 100644 index 220906294..000000000 Binary files a/GitTest_DataModel.xcdatamodel/elements and /dev/null differ diff --git a/GitTest_DataModel.xcdatamodel/layout b/GitTest_DataModel.xcdatamodel/layout deleted file mode 100644 index 85196a859..000000000 Binary files a/GitTest_DataModel.xcdatamodel/layout and /dev/null differ diff --git a/GitX.xcodeproj/.gitignore b/GitX.xcodeproj/.gitignore new file mode 100644 index 000000000..a213f5731 --- /dev/null +++ b/GitX.xcodeproj/.gitignore @@ -0,0 +1,3 @@ +xcuserdata/ +*.xccheckout + diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 8f8f62ce3..3f049ba6a 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -3,392 +3,541 @@ archiveVersion = 1; classes = { }; - objectVersion = 45; + objectVersion = 46; objects = { /* Begin PBXAggregateTarget section */ - F56439F70F792B2100A579C2 /* Generate PList Prefix */ = { + 4D3F252B18C37D2E000922D9 /* Generate Scripting Header */ = { isa = PBXAggregateTarget; - buildConfigurationList = F56439FD0F792B3600A579C2 /* Build configuration list for PBXAggregateTarget "Generate PList Prefix" */; + buildConfigurationList = 4D3F252C18C37D2E000922D9 /* Build configuration list for PBXAggregateTarget "Generate Scripting Header" */; buildPhases = ( - F56439F60F792B2100A579C2 /* ShellScript */, + 4D3F252F18C37D5A000922D9 /* Generate Scripting Header */, ); dependencies = ( ); - name = "Generate PList Prefix"; - productName = "Generate PList Prefix"; + name = "Generate Scripting Header"; + productName = "Generate Scripting Header"; }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 056438B70ED0C40B00985397 /* DetailViewTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = 056438B60ED0C40B00985397 /* DetailViewTemplate.png */; }; - 3BC07F4C0ED5A5C5009A7768 /* HistoryViewTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = 3BC07F4A0ED5A5C5009A7768 /* HistoryViewTemplate.png */; }; - 3BC07F4D0ED5A5C5009A7768 /* CommitViewTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = 3BC07F4B0ED5A5C5009A7768 /* CommitViewTemplate.png */; }; - 47DBDB580E94EDE700671A1E /* DBPrefsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 47DBDB570E94EDE700671A1E /* DBPrefsWindowController.m */; }; - 47DBDB670E94EE8B00671A1E /* PBPrefsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 47DBDB660E94EE8B00671A1E /* PBPrefsWindowController.m */; }; - 47DBDB6A0E94EF6500671A1E /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47DBDB680E94EF6500671A1E /* Preferences.xib */; }; - 47DBDBB10E94F6CA00671A1E /* Updates.png in Resources */ = {isa = PBXBuildFile; fileRef = 47DBDBB00E94F6CA00671A1E /* Updates.png */; }; - 47DBDBCA0E95016F00671A1E /* PBNSURLPathUserDefaultsTransfomer.m in Sources */ = {isa = PBXBuildFile; fileRef = 47DBDBC90E95016F00671A1E /* PBNSURLPathUserDefaultsTransfomer.m */; }; - 770B37ED0679A11B001EADE2 /* GitTest_DataModel.xcdatamodel in Sources */ = {isa = PBXBuildFile; fileRef = 770B37EC0679A11B001EADE2 /* GitTest_DataModel.xcdatamodel */; }; - 77C8280E06725ACE000B614F /* ApplicationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 77C8280C06725ACE000B614F /* ApplicationController.m */; }; - 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; - 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + 0A6858C711F7EA8A00AC2BE4 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A6858C611F7EA8A00AC2BE4 /* CoreServices.framework */; }; + 2682AABB1929140E00271A4D /* GTOID+JavaScript.m in Sources */ = {isa = PBXBuildFile; fileRef = 2682AABA1929140E00271A4D /* GTOID+JavaScript.m */; }; + 373F9A201EAF97A200A77B4C /* NSSplitView+GitX.m in Sources */ = {isa = PBXBuildFile; fileRef = 373F9A1F1EAF97A200A77B4C /* NSSplitView+GitX.m */; }; + 37A5657D1EAD096300CA0332 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37A5657C1EAD096300CA0332 /* Quartz.framework */; }; + 4A1F4E6917AFE969004D51E9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97325FDCFA39411CA2CEA /* Foundation.framework */; }; + 4A2125A417C0C78A00B5B582 /* NSColor+RGB.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A2125A317C0C78A00B5B582 /* NSColor+RGB.m */; }; + 4A40159714067B6300DB9C07 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A40159614067B6300DB9C07 /* CoreFoundation.framework */; }; + 4A40159914067B7A00DB9C07 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A40159814067B7A00DB9C07 /* AppKit.framework */; }; + 4A5D75DE14A9A90500DF6C68 /* mainSplitterBar.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D759B14A9A90500DF6C68 /* mainSplitterBar.tiff */; }; + 4A5D75DF14A9A90500DF6C68 /* mainSplitterDimple.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D759C14A9A90500DF6C68 /* mainSplitterDimple.tiff */; }; + 4A5D75F114A9A90500DF6C68 /* rewindImage.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75AF14A9A90500DF6C68 /* rewindImage.pdf */; }; + 4A5D75F714A9A90500DF6C68 /* source.css in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75B514A9A90500DF6C68 /* source.css */; }; + 4A5D75F914A9A90500DF6C68 /* UpdateKey.pem in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75B714A9A90500DF6C68 /* UpdateKey.pem */; }; + 4A5D75FB14A9A90500DF6C68 /* OpenRecentPopup.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75BA14A9A90500DF6C68 /* OpenRecentPopup.xib */; }; + 4A5D75FC14A9A90500DF6C68 /* PBCommitHookFailedSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75BB14A9A90500DF6C68 /* PBCommitHookFailedSheet.xib */; }; + 4A5D75FD14A9A90500DF6C68 /* PBDiffWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75BC14A9A90500DF6C68 /* PBDiffWindow.xib */; }; + 4A5D75FE14A9A90500DF6C68 /* PBGitCommitView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75BD14A9A90500DF6C68 /* PBGitCommitView.xib */; }; + 4A5D75FF14A9A90500DF6C68 /* PBGitHistoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75BE14A9A90500DF6C68 /* PBGitHistoryView.xib */; }; + 4A5D760014A9A90500DF6C68 /* PBGitSidebarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75BF14A9A90500DF6C68 /* PBGitSidebarView.xib */; }; + 4A5D760114A9A90500DF6C68 /* PBGitXMessageSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D75C014A9A90500DF6C68 /* PBGitXMessageSheet.xib */; }; + 4A5D761714A9A99E00DF6C68 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D760314A9A99E00DF6C68 /* InfoPlist.strings */; }; + 4A5D761814A9A99E00DF6C68 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D760514A9A99E00DF6C68 /* MainMenu.xib */; }; + 4A5D761914A9A99E00DF6C68 /* PBAddRemoteSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D760714A9A99E00DF6C68 /* PBAddRemoteSheet.xib */; }; + 4A5D761A14A9A99E00DF6C68 /* PBCloneRepositoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D760914A9A99E00DF6C68 /* PBCloneRepositoryPanel.xib */; }; + 4A5D761C14A9A99E00DF6C68 /* PBCreateBranchSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D760D14A9A99E00DF6C68 /* PBCreateBranchSheet.xib */; }; + 4A5D761D14A9A99E00DF6C68 /* PBCreateTagSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D760F14A9A99E00DF6C68 /* PBCreateTagSheet.xib */; }; + 4A5D761E14A9A99E00DF6C68 /* PBRemoteProgressSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D761114A9A99E00DF6C68 /* PBRemoteProgressSheet.xib */; }; + 4A5D762014A9A99E00DF6C68 /* RepositoryWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4A5D761514A9A99E00DF6C68 /* RepositoryWindow.xib */; }; + 4A5D76E114A9A9CC00DF6C68 /* ApplicationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D762714A9A9CC00DF6C68 /* ApplicationController.m */; }; + 4A5D76E314A9A9CC00DF6C68 /* OpenRecentController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D762B14A9A9CC00DF6C68 /* OpenRecentController.m */; }; + 4A5D76E414A9A9CC00DF6C68 /* PBDiffWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D762D14A9A9CC00DF6C68 /* PBDiffWindowController.m */; }; + 4A5D76E514A9A9CC00DF6C68 /* PBGitCommitController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D762F14A9A9CC00DF6C68 /* PBGitCommitController.m */; }; + 4A5D76E614A9A9CC00DF6C68 /* PBGitHistoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D763114A9A9CC00DF6C68 /* PBGitHistoryController.m */; }; + 4A5D76E814A9A9CC00DF6C68 /* PBGitSidebarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D763514A9A9CC00DF6C68 /* PBGitSidebarController.m */; }; + 4A5D76E914A9A9CC00DF6C68 /* PBGitWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D763714A9A9CC00DF6C68 /* PBGitWindowController.m */; }; + 4A5D76EA14A9A9CC00DF6C68 /* PBHistorySearchController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D763914A9A9CC00DF6C68 /* PBHistorySearchController.m */; }; + 4A5D76EC14A9A9CC00DF6C68 /* PBRefController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D763D14A9A9CC00DF6C68 /* PBRefController.m */; }; + 4A5D76ED14A9A9CC00DF6C68 /* PBRepositoryDocumentController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D763F14A9A9CC00DF6C68 /* PBRepositoryDocumentController.m */; }; + 4A5D76EE14A9A9CC00DF6C68 /* PBServicesController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764114A9A9CC00DF6C68 /* PBServicesController.m */; }; + 4A5D76EF14A9A9CC00DF6C68 /* PBViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764314A9A9CC00DF6C68 /* PBViewController.m */; }; + 4A5D76F014A9A9CC00DF6C68 /* PBWebChangesController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764514A9A9CC00DF6C68 /* PBWebChangesController.m */; }; + 4A5D76F114A9A9CC00DF6C68 /* PBWebController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764714A9A9CC00DF6C68 /* PBWebController.m */; }; + 4A5D76F214A9A9CC00DF6C68 /* PBWebDiffController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764914A9A9CC00DF6C68 /* PBWebDiffController.m */; }; + 4A5D76F314A9A9CC00DF6C68 /* PBWebHistoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764B14A9A9CC00DF6C68 /* PBWebHistoryController.m */; }; + 4A5D76F414A9A9CC00DF6C68 /* PBGitBinary.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D764F14A9A9CC00DF6C68 /* PBGitBinary.m */; }; + 4A5D76F514A9A9CC00DF6C68 /* PBGitCommit.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D765114A9A9CC00DF6C68 /* PBGitCommit.m */; }; + 4A5D76F714A9A9CC00DF6C68 /* PBGitDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D765514A9A9CC00DF6C68 /* PBGitDefaults.m */; }; + 4A5D76F814A9A9CC00DF6C68 /* PBGitGrapher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D765714A9A9CC00DF6C68 /* PBGitGrapher.mm */; }; + 4A5D76F914A9A9CC00DF6C68 /* PBGitGraphLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D765914A9A9CC00DF6C68 /* PBGitGraphLine.m */; }; + 4A5D76FA14A9A9CC00DF6C68 /* PBGitHistoryGrapher.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D765B14A9A9CC00DF6C68 /* PBGitHistoryGrapher.m */; }; + 4A5D76FB14A9A9CC00DF6C68 /* PBGitHistoryList.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D765D14A9A9CC00DF6C68 /* PBGitHistoryList.m */; }; + 4A5D76FC14A9A9CC00DF6C68 /* PBGitIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D765F14A9A9CC00DF6C68 /* PBGitIndex.m */; }; + 4A5D76FD14A9A9CC00DF6C68 /* PBGitLane.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D766114A9A9CC00DF6C68 /* PBGitLane.mm */; }; + 4A5D76FE14A9A9CC00DF6C68 /* PBGitRef.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D766314A9A9CC00DF6C68 /* PBGitRef.m */; }; + 4A5D76FF14A9A9CC00DF6C68 /* PBGitRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D766614A9A9CC00DF6C68 /* PBGitRepository.m */; }; + 4A5D770014A9A9CC00DF6C68 /* PBGitRepositoryWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D766814A9A9CC00DF6C68 /* PBGitRepositoryWatcher.m */; }; + 4A5D770214A9A9CC00DF6C68 /* PBGitRevList.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D766C14A9A9CC00DF6C68 /* PBGitRevList.mm */; }; + 4A5D770314A9A9CC00DF6C68 /* PBGitRevSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D766E14A9A9CC00DF6C68 /* PBGitRevSpecifier.m */; }; + 4A5D770514A9A9CC00DF6C68 /* PBGitSVBranchItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D767214A9A9CC00DF6C68 /* PBGitSVBranchItem.m */; }; + 4A5D770614A9A9CC00DF6C68 /* PBGitSVFolderItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D767414A9A9CC00DF6C68 /* PBGitSVFolderItem.m */; }; + 4A5D770714A9A9CC00DF6C68 /* PBGitSVOtherRevItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D767614A9A9CC00DF6C68 /* PBGitSVOtherRevItem.m */; }; + 4A5D770814A9A9CC00DF6C68 /* PBGitSVRemoteBranchItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D767814A9A9CC00DF6C68 /* PBGitSVRemoteBranchItem.m */; }; + 4A5D770914A9A9CC00DF6C68 /* PBGitSVRemoteItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D767A14A9A9CC00DF6C68 /* PBGitSVRemoteItem.m */; }; + 4A5D770A14A9A9CC00DF6C68 /* PBGitSVStageItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D767C14A9A9CC00DF6C68 /* PBGitSVStageItem.m */; }; + 4A5D770B14A9A9CC00DF6C68 /* PBGitSVTagItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D767E14A9A9CC00DF6C68 /* PBGitSVTagItem.m */; }; + 4A5D770C14A9A9CC00DF6C68 /* PBGitTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768014A9A9CC00DF6C68 /* PBGitTree.m */; }; + 4A5D770D14A9A9CC00DF6C68 /* PBError.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768214A9A9CC00DF6C68 /* PBError.m */; }; + 4A5D770E14A9A9CC00DF6C68 /* PBGitXProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768414A9A9CC00DF6C68 /* PBGitXProtocol.m */; }; + 4A5D771114A9A9CC00DF6C68 /* GitXRelativeDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768914A9A9CC00DF6C68 /* GitXRelativeDateFormatter.m */; }; + 4A5D771214A9A9CC00DF6C68 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768B14A9A9CC00DF6C68 /* main.m */; }; + 4A5D771314A9A9CC00DF6C68 /* PBChangedFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D768D14A9A9CC00DF6C68 /* PBChangedFile.m */; }; + 4A5D771514A9A9CC00DF6C68 /* PBCommitList.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D769114A9A9CC00DF6C68 /* PBCommitList.m */; }; + 4A5D771614A9A9CC00DF6C68 /* PBGraphCellInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D769314A9A9CC00DF6C68 /* PBGraphCellInfo.m */; }; + 4A5D771714A9A9CC00DF6C68 /* PBNSURLPathUserDefaultsTransfomer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D769514A9A9CC00DF6C68 /* PBNSURLPathUserDefaultsTransfomer.m */; }; + 4A5D771B14A9A9CC00DF6C68 /* PBUnsortableTableHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D769F14A9A9CC00DF6C68 /* PBUnsortableTableHeader.m */; }; + 4A5D771D14A9A9CC00DF6C68 /* NSApplication+GitXScripting.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76A614A9A9CC00DF6C68 /* NSApplication+GitXScripting.m */; }; + 4A5D771E14A9A9CC00DF6C68 /* NSFileHandleExt.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76A814A9A9CC00DF6C68 /* NSFileHandleExt.m */; }; + 4A5D771F14A9A9CC00DF6C68 /* NSOutlineViewExt.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76AA14A9A9CC00DF6C68 /* NSOutlineViewExt.m */; }; + 4A5D772114A9A9CC00DF6C68 /* NSString_Truncate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76AE14A9A9CC00DF6C68 /* NSString_Truncate.m */; }; + 4A5D772214A9A9CC00DF6C68 /* PBEasyFS.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76B014A9A9CC00DF6C68 /* PBEasyFS.m */; }; + 4A5D772314A9A9CC00DF6C68 /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76B214A9A9CC00DF6C68 /* PBEasyPipe.m */; }; + 4A5D772414A9A9CC00DF6C68 /* GitXTextFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76B514A9A9CC00DF6C68 /* GitXTextFieldCell.m */; }; + 4A5D772514A9A9CC00DF6C68 /* GLFileView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76B714A9A9CC00DF6C68 /* GLFileView.m */; }; + 4A5D772614A9A9CC00DF6C68 /* PBAddRemoteSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76B914A9A9CC00DF6C68 /* PBAddRemoteSheet.m */; }; + 4A5D772714A9A9CC00DF6C68 /* PBCloneRepositoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76BB14A9A9CC00DF6C68 /* PBCloneRepositoryPanel.m */; }; + 4A5D772A14A9A9CC00DF6C68 /* PBCommitHookFailedSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76C114A9A9CC00DF6C68 /* PBCommitHookFailedSheet.m */; }; + 4A5D772B14A9A9CC00DF6C68 /* PBCommitMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76C314A9A9CC00DF6C68 /* PBCommitMessageView.m */; }; + 4A5D772C14A9A9CC00DF6C68 /* PBCreateBranchSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76C514A9A9CC00DF6C68 /* PBCreateBranchSheet.m */; }; + 4A5D772D14A9A9CC00DF6C68 /* PBCreateTagSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76C714A9A9CC00DF6C68 /* PBCreateTagSheet.m */; }; + 4A5D772E14A9A9CC00DF6C68 /* PBFileChangesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76C914A9A9CC00DF6C68 /* PBFileChangesTableView.m */; }; + 4A5D772F14A9A9CC00DF6C68 /* PBGitGradientBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76CB14A9A9CC00DF6C68 /* PBGitGradientBarView.m */; }; + 4A5D773014A9A9CC00DF6C68 /* PBGitRevisionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76CD14A9A9CC00DF6C68 /* PBGitRevisionCell.m */; }; + 4A5D773114A9A9CC00DF6C68 /* PBGitXMessageSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76CF14A9A9CC00DF6C68 /* PBGitXMessageSheet.m */; }; + 4A5D773214A9A9CC00DF6C68 /* PBIconAndTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76D114A9A9CC00DF6C68 /* PBIconAndTextCell.m */; }; + 4A5D773414A9A9CC00DF6C68 /* PBQLOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76D514A9A9CC00DF6C68 /* PBQLOutlineView.m */; }; + 4A5D773514A9A9CC00DF6C68 /* PBQLTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76D714A9A9CC00DF6C68 /* PBQLTextView.m */; }; + 4A5D773614A9A9CC00DF6C68 /* PBRefMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76D914A9A9CC00DF6C68 /* PBRefMenuItem.m */; }; + 4A5D773714A9A9CC00DF6C68 /* PBRemoteProgressSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76DB14A9A9CC00DF6C68 /* PBRemoteProgressSheet.m */; }; + 4A5D773814A9A9CC00DF6C68 /* PBSourceViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5D76DD14A9A9CC00DF6C68 /* PBSourceViewCell.m */; }; + 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 */; }; + 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 */; }; + 4A90A73514A9D24300D0DA02 /* GitX.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 4A90A73414A9D24300D0DA02 /* GitX.sdef */; }; + 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 */; }; + 4D3FB3E7198AEAAF000B4A58 /* PBGitRepositoryDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D3FB3E6198AEAAF000B4A58 /* PBGitRepositoryDocument.m */; }; + 4D6E4F7A1E56851A004C3A6F /* PBMacros.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D6E4F781E56851A004C3A6F /* PBMacros.m */; }; + 4D843B1F1E5D27C3004C3A6F /* PBTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D843B1E1E5D27C3004C3A6F /* PBTask.m */; }; + 4D843B401E5E3939004C3A6F /* PBGitRepository_PBGitBinarySupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D843B3F1E5E3939004C3A6F /* PBGitRepository_PBGitBinarySupport.m */; }; + 4DECFBB21E662962004C3A6F /* ObjectiveGit+PBCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DECFBB11E662962004C3A6F /* ObjectiveGit+PBCategories.m */; }; + 551BF176112F3F4B00265053 /* gitx_askpasswd in Resources */ = {isa = PBXBuildFile; fileRef = 551BF111112F371800265053 /* gitx_askpasswd */; }; + 630A1330215EB3A000A14FA3 /* libssl.1.0.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 630A132E215EB3A000A14FA3 /* libssl.1.0.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 630A1331215EB3A000A14FA3 /* libcrypto.1.0.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 630A132F215EB3A000A14FA3 /* libcrypto.1.0.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 630A1336215F972B00A14FA3 /* ObjectiveGit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 63663D73215D408300FBAA0A /* ObjectiveGit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 63663D74215D408300FBAA0A /* ObjectiveGit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63663D73215D408300FBAA0A /* ObjectiveGit.framework */; }; + 63663D78215D42AE00FBAA0A /* ObjectiveGit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63663D73215D408300FBAA0A /* ObjectiveGit.framework */; }; + 643952771603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 643952761603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m */; }; + 6C25810B1C2720E60080A89A /* GitXCommitCopier.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C25810A1C2720E60080A89A /* GitXCommitCopier.m */; }; + 6C4855C81D57DCFC0027A7B4 /* PBTerminalUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C4855C71D57DCFC0027A7B4 /* PBTerminalUtil.m */; }; + 6C4855DA1D57E4FA0027A7B4 /* PBOpenShallowRepositoryErrorRecoveryAttempter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C4855D91D57E4FA0027A7B4 /* PBOpenShallowRepositoryErrorRecoveryAttempter.m */; }; + 6C5244131E00C66E0051DE20 /* PBHistorySearchMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C5244121E00C66E0051DE20 /* PBHistorySearchMode.m */; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; - 911111E20E58BD5A00BF76B4 /* RepositoryWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 911111E00E58BD5A00BF76B4 /* RepositoryWindow.xib */; }; - 911111F80E594F3F00BF76B4 /* PBRepositoryDocumentController.m in Sources */ = {isa = PBXBuildFile; fileRef = 911111F70E594F3F00BF76B4 /* PBRepositoryDocumentController.m */; }; 911112370E5A097800BF76B4 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 911112360E5A097800BF76B4 /* Security.framework */; }; - 913D5E4D0E55644E00CECEA2 /* gitx.m in Sources */ = {isa = PBXBuildFile; fileRef = 913D5E440E55640C00CECEA2 /* gitx.m */; }; 913D5E500E55645900CECEA2 /* gitx in Resources */ = {isa = PBXBuildFile; fileRef = 913D5E490E55644600CECEA2 /* gitx */; }; - 913D5E5F0E556A9300CECEA2 /* PBCLIProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 913D5E5E0E556A9300CECEA2 /* PBCLIProxy.m */; }; - 91B103CC0E898EC300C84364 /* PBIconAndTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 91B103CB0E898EC300C84364 /* PBIconAndTextCell.m */; }; - 93CB42C20EAB7B2200530609 /* PBGitDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CB42C10EAB7B2200530609 /* PBGitDefaults.m */; }; - 93F7857F0EA3ABF100C1F443 /* PBCommitMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F7857E0EA3ABF100C1F443 /* PBCommitMessageView.m */; }; - D26DC6450E782C9000C777B2 /* gitx.icns in Resources */ = {isa = PBXBuildFile; fileRef = D26DC6440E782C9000C777B2 /* gitx.icns */; }; - EB2A734A0FEE3F09006601CF /* PBCollapsibleSplitView.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2A73490FEE3F09006601CF /* PBCollapsibleSplitView.m */; }; - F50A411F0EBB874C00208746 /* mainSplitterBar.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F50A411D0EBB874C00208746 /* mainSplitterBar.tiff */; }; - F50A41200EBB874C00208746 /* mainSplitterDimple.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F50A411E0EBB874C00208746 /* mainSplitterDimple.tiff */; }; - F50A41230EBB875D00208746 /* PBNiceSplitView.m in Sources */ = {isa = PBXBuildFile; fileRef = F50A41220EBB875D00208746 /* PBNiceSplitView.m */; }; - F50FE0E30E07BE9600854FCD /* PBGitRevisionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F50FE0E20E07BE9600854FCD /* PBGitRevisionCell.m */; }; - F513085B0E0740F2000C8BCD /* PBQLOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = F513085A0E0740F2000C8BCD /* PBQLOutlineView.m */; }; - F5140DC90E8A8EB20091E9F3 /* RoundedRectangle.m in Sources */ = {isa = PBXBuildFile; fileRef = F5140DC80E8A8EB20091E9F3 /* RoundedRectangle.m */; }; - F523CEB60ED3399200DDD714 /* PBGitIndexController.m in Sources */ = {isa = PBXBuildFile; fileRef = F523CEB50ED3399200DDD714 /* PBGitIndexController.m */; }; - F52BCE030E84208300AA3741 /* PBGitHistoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F52BCE020E84208300AA3741 /* PBGitHistoryView.xib */; }; - F52BCE070E84211300AA3741 /* PBGitHistoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = F52BCE060E84211300AA3741 /* PBGitHistoryController.m */; }; - F53C4DF70E97FC630022AD59 /* PBGitBinary.m in Sources */ = {isa = PBXBuildFile; fileRef = F53C4DF60E97FC630022AD59 /* PBGitBinary.m */; }; - F53C4DF80E97FCA70022AD59 /* PBGitBinary.m in Sources */ = {isa = PBXBuildFile; fileRef = F53C4DF60E97FC630022AD59 /* PBGitBinary.m */; }; - F53C4DF90E97FCAD0022AD59 /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */; }; - F53FF2050E7ABB5300389171 /* PBGitRevSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F53FF2040E7ABB5300389171 /* PBGitRevSpecifier.m */; }; - F56174570E058893001DCD79 /* PBGitTree.m in Sources */ = {isa = PBXBuildFile; fileRef = F56174560E058893001DCD79 /* PBGitTree.m */; }; - F56244090E9684B0002B6C44 /* PBUnsortableTableHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = F56244080E9684B0002B6C44 /* PBUnsortableTableHeader.m */; }; - F562C8870FE1766C000EC528 /* NSString_RegEx.m in Sources */ = {isa = PBXBuildFile; fileRef = F562C8860FE1766C000EC528 /* NSString_RegEx.m */; }; - F56524BB0E02D22D00F03B52 /* NSFileHandleExt.m in Sources */ = {isa = PBXBuildFile; fileRef = F56524B90E02D22D00F03B52 /* NSFileHandleExt.m */; }; - F56524F00E02D45200F03B52 /* PBGitCommit.m in Sources */ = {isa = PBXBuildFile; fileRef = F56524EF0E02D45200F03B52 /* PBGitCommit.m */; }; + 978357A6189C05B100ADC689 /* FolderClosedTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357A4189C05B100ADC689 /* FolderClosedTemplate.pdf */; }; + 978357A7189C05B100ADC689 /* FolderTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357A5189C05B100ADC689 /* FolderTemplate.pdf */; }; + 978357AD189C074500ADC689 /* BranchTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357A8189C074500ADC689 /* BranchTemplate.pdf */; }; + 978357AE189C074500ADC689 /* RemoteBranchTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357A9189C074500ADC689 /* RemoteBranchTemplate.pdf */; }; + 978357AF189C074500ADC689 /* RemoteTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357AA189C074500ADC689 /* RemoteTemplate.pdf */; }; + 978357B0189C074500ADC689 /* StageTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357AB189C074500ADC689 /* StageTemplate.pdf */; }; + 978357B1189C074500ADC689 /* TagTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357AC189C074500ADC689 /* TagTemplate.pdf */; }; + 978357B8189C19A000ADC689 /* AddBranchTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357B2189C19A000ADC689 /* AddBranchTemplate.pdf */; }; + 978357B9189C19A000ADC689 /* AddLabelTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357B3189C19A000ADC689 /* AddLabelTemplate.pdf */; }; + 978357BA189C19A000ADC689 /* AddRemoteTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357B4189C19A000ADC689 /* AddRemoteTemplate.pdf */; }; + 978357BB189C19A000ADC689 /* FetchTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357B5189C19A000ADC689 /* FetchTemplate.pdf */; }; + 978357BC189C19A000ADC689 /* PullTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357B6189C19A000ADC689 /* PullTemplate.pdf */; }; + 978357BD189C19A000ADC689 /* PushTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357B7189C19A000ADC689 /* PushTemplate.pdf */; }; + 978357C1189C1F5700ADC689 /* CherryPickTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357BE189C1F5700ADC689 /* CherryPickTemplate.pdf */; }; + 978357C2189C1F5700ADC689 /* MergeTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357BF189C1F5700ADC689 /* MergeTemplate.pdf */; }; + 978357C3189C1F5700ADC689 /* RebaseTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357C0189C1F5700ADC689 /* RebaseTemplate.pdf */; }; + 978357C5189C238300ADC689 /* DetailViewTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357C4189C238300ADC689 /* DetailViewTemplate.pdf */; }; + 978357C7189C23AF00ADC689 /* TreeViewTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 978357C6189C23AF00ADC689 /* TreeViewTemplate.pdf */; }; + 97CF01F918A6C5BB00E30F2B /* deleted_file.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 97CF01F618A6C5BB00E30F2B /* deleted_file.pdf */; }; + 97CF01FA18A6C5BB00E30F2B /* empty_file.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 97CF01F718A6C5BB00E30F2B /* empty_file.pdf */; }; + 97CF01FB18A6C5BB00E30F2B /* new_file.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 97CF01F818A6C5BB00E30F2B /* new_file.pdf */; }; + A2F8D0DF17AAB32500580B84 /* PBGitStash.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F8D0DE17AAB32500580B84 /* PBGitStash.m */; }; + A2F8D0EB17AAB95E00580B84 /* PBGitSVStashItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F8D0EA17AAB95E00580B84 /* PBGitSVStashItem.m */; }; + BF42F1331E025403004769FF /* GitXTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = BF42F1321E025403004769FF /* GitXTextView.m */; }; + BFFCB5471E3EFA9F00E0B254 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFFCB5461E3EFA9F00E0B254 /* Images.xcassets */; }; + D87127011229A21C00012334 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D87127001229A21C00012334 /* QuartzCore.framework */; }; + D89E9B141218BA260097A90B /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */; }; + D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */; }; F56526240E03D85900F03B52 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F56526230E03D85900F03B52 /* WebKit.framework */; }; - F565262B0E03D89B00F03B52 /* PBWebHistoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = F565262A0E03D89B00F03B52 /* PBWebHistoryController.m */; }; - F567CC64106E6BC80059BB9D /* PBGitRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = F5945E160E02B0C200706420 /* PBGitRepository.m */; }; - F567CC65106E6BC90059BB9D /* PBGitBinary.m in Sources */ = {isa = PBXBuildFile; fileRef = F53C4DF60E97FC630022AD59 /* PBGitBinary.m */; }; - F567CC66106E6BC90059BB9D /* PBGitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 93FCCBA80EA8AF450061B02B /* PBGitConfig.m */; }; - F567CC67106E6BD00059BB9D /* PBGitRepository.h in Headers */ = {isa = PBXBuildFile; fileRef = F5945E150E02B0C200706420 /* PBGitRepository.h */; }; - F567CC68106E6BD00059BB9D /* PBGitBinary.h in Headers */ = {isa = PBXBuildFile; fileRef = F53C4DF50E97FC630022AD59 /* PBGitBinary.h */; }; - F567CC69106E6BD00059BB9D /* PBGitConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = F5D2DC850EA401A80034AD24 /* PBGitConfig.h */; }; - F567CC7B106E6BF70059BB9D /* PBGitRef.h in Headers */ = {isa = PBXBuildFile; fileRef = F5C007730E731B48007B84B2 /* PBGitRef.h */; }; - F567CC7F106E6C470059BB9D /* libgit2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5C580E40EDA250900995434 /* libgit2.a */; }; - F567CC8E106E6FC40059BB9D /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */; }; - F569AE930F2CBD7C00C2FFA7 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = F569AE920F2CBD7C00C2FFA7 /* Credits.html */; }; - F56ADDD90ED19F9E002AC78F /* AddBranchTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = F56ADDD70ED19F9E002AC78F /* AddBranchTemplate.png */; }; - F56ADDDA0ED19F9E002AC78F /* AddLabelTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = F56ADDD80ED19F9E002AC78F /* AddLabelTemplate.png */; }; - F56CC7320E65E0E5004307B4 /* PBGraphCellInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */; }; - F57240BB0E9678EA00D8EE66 /* deleted_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F57240BA0E9678EA00D8EE66 /* deleted_file.png */; }; - F574A2850EAE2EAC003F2CB1 /* PBRefController.m in Sources */ = {isa = PBXBuildFile; fileRef = F574A2840EAE2EAC003F2CB1 /* PBRefController.m */; }; - F574A2910EAE2FF4003F2CB1 /* PBGitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 93FCCBA80EA8AF450061B02B /* PBGitConfig.m */; }; - F57CC3910E05DDF2000472E2 /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */; }; - F57CC4410E05E496000472E2 /* PBGitWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC4400E05E496000472E2 /* PBGitWindowController.m */; }; - F580E6AE0E733276009E2D3F /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F580E6AD0E733276009E2D3F /* Sparkle.framework */; }; - F580E6B10E73328C009E2D3F /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = F580E6AD0E733276009E2D3F /* Sparkle.framework */; }; - F5886A160ED5D3490066E74C /* speedtest.m in Sources */ = {isa = PBXBuildFile; fileRef = F5886A0A0ED5D27A0066E74C /* speedtest.m */; }; - F5886A170ED5D34F0066E74C /* PBGitRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = F5945E160E02B0C200706420 /* PBGitRepository.m */; }; - F5886A180ED5D3540066E74C /* PBGitRevList.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E170E0829C20006317A /* PBGitRevList.mm */; }; - F5886A190ED5D3560066E74C /* PBGitRef.m in Sources */ = {isa = PBXBuildFile; fileRef = F5C007740E731B48007B84B2 /* PBGitRef.m */; }; - F5886A1B0ED5D37C0066E74C /* NSFileHandleExt.m in Sources */ = {isa = PBXBuildFile; fileRef = F56524B90E02D22D00F03B52 /* NSFileHandleExt.m */; }; - F5886A1C0ED5D37C0066E74C /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */; }; - F5886A1D0ED5D37C0066E74C /* PBEasyFS.m in Sources */ = {isa = PBXBuildFile; fileRef = F5DFFA6B0E075D8800617813 /* PBEasyFS.m */; }; - F5886A1E0ED5D3880066E74C /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; - F5886A260ED5D4870066E74C /* PBGitGrapher.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E790E082E440006317A /* PBGitGrapher.mm */; }; - F5886A270ED5D4870066E74C /* PBGitRevisionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F50FE0E20E07BE9600854FCD /* PBGitRevisionCell.m */; }; - F5886A290ED5D4870066E74C /* PBGraphCellInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */; }; - F5886A2A0ED5D4870066E74C /* PBGitLane.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5C6F68C0E65FF9300478D97 /* PBGitLane.mm */; }; - F5886A310ED5D54C0066E74C /* PBGitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 93FCCBA80EA8AF450061B02B /* PBGitConfig.m */; }; - F5886A320ED5D5510066E74C /* PBGitCommit.m in Sources */ = {isa = PBXBuildFile; fileRef = F56524EF0E02D45200F03B52 /* PBGitCommit.m */; }; - F5886A330ED5D5580066E74C /* PBGitRevSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F53FF2040E7ABB5300389171 /* PBGitRevSpecifier.m */; }; - F5886A340ED5D55D0066E74C /* PBGitBinary.m in Sources */ = {isa = PBXBuildFile; fileRef = F53C4DF60E97FC630022AD59 /* PBGitBinary.m */; }; - F5886A360ED5D56E0066E74C /* PBGitTree.m in Sources */ = {isa = PBXBuildFile; fileRef = F56174560E058893001DCD79 /* PBGitTree.m */; }; - F59116E60E843BB50072CCB1 /* PBGitCommitView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F59116E50E843BB50072CCB1 /* PBGitCommitView.xib */; }; - F59116E90E843BCB0072CCB1 /* PBGitCommitController.m in Sources */ = {isa = PBXBuildFile; fileRef = F59116E80E843BCB0072CCB1 /* PBGitCommitController.m */; }; - F593DF780E9E636C003A8559 /* PBFileChangesTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = F593DF770E9E636C003A8559 /* PBFileChangesTableView.m */; }; - F5945E170E02B0C200706420 /* PBGitRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = F5945E160E02B0C200706420 /* PBGitRepository.m */; }; - F59F1DD5105C4FF300115F88 /* PBGitIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = F59F1DD4105C4FF300115F88 /* PBGitIndex.m */; }; - F5AD56790E79B78100EDAAFE /* PBCommitList.m in Sources */ = {isa = PBXBuildFile; fileRef = F5AD56780E79B78100EDAAFE /* PBCommitList.m */; }; - F5B721C40E05CF7E00AF29DC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F5B721C20E05CF7E00AF29DC /* MainMenu.xib */; }; - F5C007750E731B48007B84B2 /* PBGitRef.m in Sources */ = {isa = PBXBuildFile; fileRef = F5C007740E731B48007B84B2 /* PBGitRef.m */; }; - F5C580E50EDA250900995434 /* libgit2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5C580E40EDA250900995434 /* libgit2.a */; }; - F5C580F50EDA251100995434 /* libgit2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5C580E40EDA250900995434 /* libgit2.a */; }; - F5C6F68D0E65FF9300478D97 /* PBGitLane.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5C6F68C0E65FF9300478D97 /* PBGitLane.mm */; }; - F5D376B9107511C500AAAC80 /* PBGitIndex.m in Sources */ = {isa = PBXBuildFile; fileRef = F59F1DD4105C4FF300115F88 /* PBGitIndex.m */; }; - F5D376C91075134D00AAAC80 /* PBGitRevList.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E170E0829C20006317A /* PBGitRevList.mm */; }; - F5D376CF1075139B00AAAC80 /* PBGitRef.m in Sources */ = {isa = PBXBuildFile; fileRef = F5C007740E731B48007B84B2 /* PBGitRef.m */; }; - F5D376D01075139B00AAAC80 /* PBGitRevSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = F53FF2040E7ABB5300389171 /* PBGitRevSpecifier.m */; }; - F5D376E0107516A700AAAC80 /* PBChangedFile.m in Sources */ = {isa = PBXBuildFile; fileRef = F5E927F70E883E7200056E75 /* PBChangedFile.m */; }; - F5D3772610753BD700AAAC80 /* PBChangedFile.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E927F60E883E7200056E75 /* PBChangedFile.h */; }; - F5D3772710753BD700AAAC80 /* PBGitIndex.h in Headers */ = {isa = PBXBuildFile; fileRef = F59F1DD3105C4FF300115F88 /* PBGitIndex.h */; }; - F5DFFA6C0E075D8800617813 /* PBEasyFS.m in Sources */ = {isa = PBXBuildFile; fileRef = F5DFFA6B0E075D8800617813 /* PBEasyFS.m */; }; - F5E424110EA3E4D60046E362 /* PBDiffWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = F5E424100EA3E4D60046E362 /* PBDiffWindow.xib */; }; - F5E424150EA3E4E10046E362 /* PBDiffWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5E424130EA3E4E10046E362 /* PBDiffWindowController.m */; }; - F5E424180EA3E4EB0046E362 /* PBWebDiffController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5E424160EA3E4EB0046E362 /* PBWebDiffController.m */; }; F5E4DBFB0EAB58D90013FAFC /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5E4DBFA0EAB58D90013FAFC /* SystemConfiguration.framework */; }; - F5E926060E8827D300056E75 /* PBViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5E926050E8827D300056E75 /* PBViewController.m */; }; - F5E927F80E883E7200056E75 /* PBChangedFile.m in Sources */ = {isa = PBXBuildFile; fileRef = F5E927F70E883E7200056E75 /* PBChangedFile.m */; }; - F5E927FC0E883F0700056E75 /* PBWebChangesController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5E927FB0E883F0700056E75 /* PBWebChangesController.m */; }; - F5E92A1B0E88550E00056E75 /* empty_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F5E92A1A0E88550E00056E75 /* empty_file.png */; }; - F5E92A230E88569500056E75 /* new_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F5E92A220E88569500056E75 /* new_file.png */; }; - F5EF8C8E0E9D4A5D0050906B /* PBWebController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */; }; - F5FC41F40EBCBD4300191D80 /* PBGitXProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */; }; - F5FC43FE0EBD08EE00191D80 /* PBRefMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FC43FD0EBD08EE00191D80 /* PBRefMenuItem.m */; }; - F5FE6C030EB13BC900F30D12 /* PBServicesController.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FE6C020EB13BC900F30D12 /* PBServicesController.m */; }; - F5FF4E180E0829C20006317A /* PBGitRevList.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E170E0829C20006317A /* PBGitRevList.mm */; }; - F5FF4E7A0E082E440006317A /* PBGitGrapher.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E790E082E440006317A /* PBGitGrapher.mm */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 913D5E590E5564F400CECEA2 /* PBXContainerItemProxy */ = { + 4A774CBC142F464D00E158CC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; proxyType = 1; remoteGlobalIDString = 913D5E480E55644600CECEA2; remoteInfo = "cli tool"; }; - F5643A010F792B4900A579C2 /* PBXContainerItemProxy */ = { + 4D3F253018C37D9A000922D9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; proxyType = 1; - remoteGlobalIDString = F56439F70F792B2100A579C2; - remoteInfo = "Generate PList Prefix"; + 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 */; + proxyType = 1; + remoteGlobalIDString = 551BF110112F371800265053; + remoteInfo = gitx_askpasswd; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - F580E6BD0E73329C009E2D3F /* CopyFiles */ = { + 630A132D215EB37D00A14FA3 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - F580E6B10E73328C009E2D3F /* Sparkle.framework in CopyFiles */, + 630A1330215EB3A000A14FA3 /* libssl.1.0.0.dylib in CopyFiles */, + 630A1336215F972B00A14FA3 /* ObjectiveGit.framework in CopyFiles */, + 630A1331215EB3A000A14FA3 /* libcrypto.1.0.0.dylib in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 056438B60ED0C40B00985397 /* DetailViewTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = DetailViewTemplate.png; path = Images/DetailViewTemplate.png; sourceTree = ""; }; - 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 0A6858C611F7EA8A00AC2BE4 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 2682AAB91929140E00271A4D /* GTOID+JavaScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTOID+JavaScript.h"; sourceTree = ""; }; + 2682AABA1929140E00271A4D /* GTOID+JavaScript.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTOID+JavaScript.m"; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 32CA4F630368D1EE00C91783 /* GitX_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitX_Prefix.pch; sourceTree = ""; }; - 3BC07F4A0ED5A5C5009A7768 /* HistoryViewTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = HistoryViewTemplate.png; path = Images/HistoryViewTemplate.png; sourceTree = ""; }; - 3BC07F4B0ED5A5C5009A7768 /* CommitViewTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = CommitViewTemplate.png; path = Images/CommitViewTemplate.png; sourceTree = ""; }; - 47DBDB560E94EDE700671A1E /* DBPrefsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBPrefsWindowController.h; sourceTree = ""; }; - 47DBDB570E94EDE700671A1E /* DBPrefsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBPrefsWindowController.m; sourceTree = ""; }; - 47DBDB650E94EE8B00671A1E /* PBPrefsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBPrefsWindowController.h; sourceTree = ""; }; - 47DBDB660E94EE8B00671A1E /* PBPrefsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBPrefsWindowController.m; sourceTree = ""; }; - 47DBDB690E94EF6500671A1E /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/Preferences.xib; sourceTree = ""; }; - 47DBDBB00E94F6CA00671A1E /* Updates.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Updates.png; path = Images/Preferences/Updates.png; sourceTree = ""; }; - 47DBDBC80E95016F00671A1E /* PBNSURLPathUserDefaultsTransfomer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBNSURLPathUserDefaultsTransfomer.h; sourceTree = ""; }; - 47DBDBC90E95016F00671A1E /* PBNSURLPathUserDefaultsTransfomer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBNSURLPathUserDefaultsTransfomer.m; sourceTree = ""; }; - 770B37EC0679A11B001EADE2 /* GitTest_DataModel.xcdatamodel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.xcdatamodel; path = GitTest_DataModel.xcdatamodel; sourceTree = ""; }; + 373F9A1E1EAF97A200A77B4C /* NSSplitView+GitX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSplitView+GitX.h"; sourceTree = ""; }; + 373F9A1F1EAF97A200A77B4C /* NSSplitView+GitX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSplitView+GitX.m"; sourceTree = ""; }; + 37A5657C1EAD096300CA0332 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; + 4A2125A217C0C78A00B5B582 /* NSColor+RGB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSColor+RGB.h"; sourceTree = ""; }; + 4A2125A317C0C78A00B5B582 /* NSColor+RGB.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSColor+RGB.m"; sourceTree = ""; }; + 4A40159614067B6300DB9C07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 4A40159814067B7A00DB9C07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 4A5D759B14A9A90500DF6C68 /* mainSplitterBar.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = mainSplitterBar.tiff; sourceTree = ""; }; + 4A5D759C14A9A90500DF6C68 /* mainSplitterDimple.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = mainSplitterDimple.tiff; sourceTree = ""; }; + 4A5D75AF14A9A90500DF6C68 /* rewindImage.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rewindImage.pdf; sourceTree = ""; }; + 4A5D75B414A9A90500DF6C68 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4A5D75B514A9A90500DF6C68 /* source.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = source.css; sourceTree = ""; }; + 4A5D75B714A9A90500DF6C68 /* UpdateKey.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = UpdateKey.pem; sourceTree = ""; }; + 4A5D75BA14A9A90500DF6C68 /* OpenRecentPopup.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OpenRecentPopup.xib; sourceTree = ""; }; + 4A5D75BB14A9A90500DF6C68 /* PBCommitHookFailedSheet.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PBCommitHookFailedSheet.xib; sourceTree = ""; }; + 4A5D75BC14A9A90500DF6C68 /* PBDiffWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PBDiffWindow.xib; sourceTree = ""; }; + 4A5D75BD14A9A90500DF6C68 /* PBGitCommitView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PBGitCommitView.xib; sourceTree = ""; }; + 4A5D75BE14A9A90500DF6C68 /* PBGitHistoryView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PBGitHistoryView.xib; sourceTree = ""; }; + 4A5D75BF14A9A90500DF6C68 /* PBGitSidebarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PBGitSidebarView.xib; sourceTree = ""; }; + 4A5D75C014A9A90500DF6C68 /* PBGitXMessageSheet.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PBGitXMessageSheet.xib; sourceTree = ""; }; + 4A5D760414A9A99E00DF6C68 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = InfoPlist.strings; sourceTree = ""; }; + 4A5D762614A9A9CC00DF6C68 /* ApplicationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplicationController.h; sourceTree = ""; }; + 4A5D762714A9A9CC00DF6C68 /* ApplicationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ApplicationController.m; sourceTree = ""; }; + 4A5D762A14A9A9CC00DF6C68 /* OpenRecentController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenRecentController.h; sourceTree = ""; }; + 4A5D762B14A9A9CC00DF6C68 /* OpenRecentController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OpenRecentController.m; sourceTree = ""; }; + 4A5D762C14A9A9CC00DF6C68 /* PBDiffWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBDiffWindowController.h; sourceTree = ""; }; + 4A5D762D14A9A9CC00DF6C68 /* PBDiffWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBDiffWindowController.m; sourceTree = ""; }; + 4A5D762E14A9A9CC00DF6C68 /* PBGitCommitController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitCommitController.h; sourceTree = ""; }; + 4A5D762F14A9A9CC00DF6C68 /* PBGitCommitController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitCommitController.m; sourceTree = ""; }; + 4A5D763014A9A9CC00DF6C68 /* PBGitHistoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PBGitHistoryController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4A5D763114A9A9CC00DF6C68 /* PBGitHistoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitHistoryController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D763414A9A9CC00DF6C68 /* PBGitSidebarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSidebarController.h; sourceTree = ""; }; + 4A5D763514A9A9CC00DF6C68 /* PBGitSidebarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSidebarController.m; sourceTree = ""; }; + 4A5D763614A9A9CC00DF6C68 /* PBGitWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitWindowController.h; sourceTree = ""; }; + 4A5D763714A9A9CC00DF6C68 /* PBGitWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitWindowController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D763814A9A9CC00DF6C68 /* PBHistorySearchController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBHistorySearchController.h; sourceTree = ""; }; + 4A5D763914A9A9CC00DF6C68 /* PBHistorySearchController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBHistorySearchController.m; sourceTree = ""; }; + 4A5D763C14A9A9CC00DF6C68 /* PBRefController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRefController.h; sourceTree = ""; }; + 4A5D763D14A9A9CC00DF6C68 /* PBRefController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRefController.m; sourceTree = ""; }; + 4A5D763E14A9A9CC00DF6C68 /* PBRepositoryDocumentController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRepositoryDocumentController.h; sourceTree = ""; }; + 4A5D763F14A9A9CC00DF6C68 /* PBRepositoryDocumentController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRepositoryDocumentController.m; sourceTree = ""; }; + 4A5D764014A9A9CC00DF6C68 /* PBServicesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBServicesController.h; sourceTree = ""; }; + 4A5D764114A9A9CC00DF6C68 /* PBServicesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBServicesController.m; sourceTree = ""; }; + 4A5D764214A9A9CC00DF6C68 /* PBViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBViewController.h; sourceTree = ""; }; + 4A5D764314A9A9CC00DF6C68 /* PBViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBViewController.m; sourceTree = ""; }; + 4A5D764414A9A9CC00DF6C68 /* PBWebChangesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebChangesController.h; sourceTree = ""; }; + 4A5D764514A9A9CC00DF6C68 /* PBWebChangesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebChangesController.m; sourceTree = ""; }; + 4A5D764614A9A9CC00DF6C68 /* PBWebController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebController.h; sourceTree = ""; }; + 4A5D764714A9A9CC00DF6C68 /* PBWebController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebController.m; sourceTree = ""; }; + 4A5D764814A9A9CC00DF6C68 /* PBWebDiffController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebDiffController.h; sourceTree = ""; }; + 4A5D764914A9A9CC00DF6C68 /* PBWebDiffController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebDiffController.m; sourceTree = ""; }; + 4A5D764A14A9A9CC00DF6C68 /* PBWebHistoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PBWebHistoryController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4A5D764B14A9A9CC00DF6C68 /* PBWebHistoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBWebHistoryController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D764E14A9A9CC00DF6C68 /* PBGitBinary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitBinary.h; sourceTree = ""; }; + 4A5D764F14A9A9CC00DF6C68 /* PBGitBinary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitBinary.m; sourceTree = ""; }; + 4A5D765014A9A9CC00DF6C68 /* PBGitCommit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PBGitCommit.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4A5D765114A9A9CC00DF6C68 /* PBGitCommit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitCommit.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D765414A9A9CC00DF6C68 /* PBGitDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitDefaults.h; sourceTree = ""; }; + 4A5D765514A9A9CC00DF6C68 /* PBGitDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitDefaults.m; sourceTree = ""; }; + 4A5D765614A9A9CC00DF6C68 /* PBGitGrapher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGrapher.h; sourceTree = ""; }; + 4A5D765714A9A9CC00DF6C68 /* PBGitGrapher.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = PBGitGrapher.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4A5D765814A9A9CC00DF6C68 /* PBGitGraphLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGraphLine.h; sourceTree = ""; }; + 4A5D765914A9A9CC00DF6C68 /* PBGitGraphLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitGraphLine.m; sourceTree = ""; }; + 4A5D765A14A9A9CC00DF6C68 /* PBGitHistoryGrapher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitHistoryGrapher.h; sourceTree = ""; }; + 4A5D765B14A9A9CC00DF6C68 /* PBGitHistoryGrapher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitHistoryGrapher.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D765C14A9A9CC00DF6C68 /* PBGitHistoryList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PBGitHistoryList.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4A5D765D14A9A9CC00DF6C68 /* PBGitHistoryList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitHistoryList.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D765E14A9A9CC00DF6C68 /* PBGitIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitIndex.h; sourceTree = ""; }; + 4A5D765F14A9A9CC00DF6C68 /* PBGitIndex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitIndex.m; sourceTree = ""; }; + 4A5D766014A9A9CC00DF6C68 /* PBGitLane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitLane.h; sourceTree = ""; }; + 4A5D766114A9A9CC00DF6C68 /* PBGitLane.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PBGitLane.mm; sourceTree = ""; }; + 4A5D766214A9A9CC00DF6C68 /* PBGitRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRef.h; sourceTree = ""; }; + 4A5D766314A9A9CC00DF6C68 /* PBGitRef.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRef.m; sourceTree = ""; }; + 4A5D766414A9A9CC00DF6C68 /* PBGitRefish.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRefish.h; sourceTree = ""; }; + 4A5D766514A9A9CC00DF6C68 /* PBGitRepository.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PBGitRepository.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4A5D766614A9A9CC00DF6C68 /* PBGitRepository.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitRepository.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D766714A9A9CC00DF6C68 /* PBGitRepositoryWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRepositoryWatcher.h; sourceTree = ""; }; + 4A5D766814A9A9CC00DF6C68 /* PBGitRepositoryWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRepositoryWatcher.m; sourceTree = ""; }; + 4A5D766B14A9A9CC00DF6C68 /* PBGitRevList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevList.h; sourceTree = ""; }; + 4A5D766C14A9A9CC00DF6C68 /* PBGitRevList.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = PBGitRevList.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 4A5D766D14A9A9CC00DF6C68 /* PBGitRevSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevSpecifier.h; sourceTree = ""; }; + 4A5D766E14A9A9CC00DF6C68 /* PBGitRevSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRevSpecifier.m; sourceTree = ""; }; + 4A5D767114A9A9CC00DF6C68 /* PBGitSVBranchItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVBranchItem.h; sourceTree = ""; }; + 4A5D767214A9A9CC00DF6C68 /* PBGitSVBranchItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVBranchItem.m; sourceTree = ""; }; + 4A5D767314A9A9CC00DF6C68 /* PBGitSVFolderItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVFolderItem.h; sourceTree = ""; }; + 4A5D767414A9A9CC00DF6C68 /* PBGitSVFolderItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVFolderItem.m; sourceTree = ""; }; + 4A5D767514A9A9CC00DF6C68 /* PBGitSVOtherRevItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVOtherRevItem.h; sourceTree = ""; }; + 4A5D767614A9A9CC00DF6C68 /* PBGitSVOtherRevItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVOtherRevItem.m; sourceTree = ""; }; + 4A5D767714A9A9CC00DF6C68 /* PBGitSVRemoteBranchItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVRemoteBranchItem.h; sourceTree = ""; }; + 4A5D767814A9A9CC00DF6C68 /* PBGitSVRemoteBranchItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVRemoteBranchItem.m; sourceTree = ""; }; + 4A5D767914A9A9CC00DF6C68 /* PBGitSVRemoteItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVRemoteItem.h; sourceTree = ""; }; + 4A5D767A14A9A9CC00DF6C68 /* PBGitSVRemoteItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVRemoteItem.m; sourceTree = ""; }; + 4A5D767B14A9A9CC00DF6C68 /* PBGitSVStageItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVStageItem.h; sourceTree = ""; }; + 4A5D767C14A9A9CC00DF6C68 /* PBGitSVStageItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVStageItem.m; sourceTree = ""; }; + 4A5D767D14A9A9CC00DF6C68 /* PBGitSVTagItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVTagItem.h; sourceTree = ""; }; + 4A5D767E14A9A9CC00DF6C68 /* PBGitSVTagItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVTagItem.m; sourceTree = ""; }; + 4A5D767F14A9A9CC00DF6C68 /* PBGitTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitTree.h; sourceTree = ""; }; + 4A5D768014A9A9CC00DF6C68 /* PBGitTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitTree.m; sourceTree = ""; }; + 4A5D768114A9A9CC00DF6C68 /* PBError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBError.h; sourceTree = ""; }; + 4A5D768214A9A9CC00DF6C68 /* PBError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBError.m; sourceTree = ""; }; + 4A5D768314A9A9CC00DF6C68 /* PBGitXProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitXProtocol.h; sourceTree = ""; }; + 4A5D768414A9A9CC00DF6C68 /* PBGitXProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitXProtocol.m; sourceTree = ""; }; + 4A5D768614A9A9CC00DF6C68 /* gitx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = gitx.m; sourceTree = ""; }; + 4A5D768714A9A9CC00DF6C68 /* gitx_askpasswd_main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = gitx_askpasswd_main.m; sourceTree = ""; }; + 4A5D768814A9A9CC00DF6C68 /* GitXRelativeDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXRelativeDateFormatter.h; sourceTree = ""; }; + 4A5D768914A9A9CC00DF6C68 /* GitXRelativeDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXRelativeDateFormatter.m; sourceTree = ""; }; + 4A5D768A14A9A9CC00DF6C68 /* GitXScriptingConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXScriptingConstants.h; sourceTree = ""; }; + 4A5D768B14A9A9CC00DF6C68 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 4A5D768C14A9A9CC00DF6C68 /* PBChangedFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBChangedFile.h; sourceTree = ""; }; + 4A5D768D14A9A9CC00DF6C68 /* PBChangedFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBChangedFile.m; sourceTree = ""; }; + 4A5D769014A9A9CC00DF6C68 /* PBCommitList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCommitList.h; sourceTree = ""; }; + 4A5D769114A9A9CC00DF6C68 /* PBCommitList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCommitList.m; sourceTree = ""; }; + 4A5D769214A9A9CC00DF6C68 /* PBGraphCellInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGraphCellInfo.h; sourceTree = ""; }; + 4A5D769314A9A9CC00DF6C68 /* PBGraphCellInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGraphCellInfo.m; sourceTree = ""; }; + 4A5D769414A9A9CC00DF6C68 /* PBNSURLPathUserDefaultsTransfomer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBNSURLPathUserDefaultsTransfomer.h; sourceTree = ""; }; + 4A5D769514A9A9CC00DF6C68 /* PBNSURLPathUserDefaultsTransfomer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBNSURLPathUserDefaultsTransfomer.m; sourceTree = ""; }; + 4A5D769614A9A9CC00DF6C68 /* PBRefContextDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRefContextDelegate.h; sourceTree = ""; }; + 4A5D769E14A9A9CC00DF6C68 /* PBUnsortableTableHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBUnsortableTableHeader.h; sourceTree = ""; }; + 4A5D769F14A9A9CC00DF6C68 /* PBUnsortableTableHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBUnsortableTableHeader.m; sourceTree = ""; }; + 4A5D76A314A9A9CC00DF6C68 /* Terminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Terminal.h; sourceTree = ""; }; + 4A5D76A514A9A9CC00DF6C68 /* NSApplication+GitXScripting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSApplication+GitXScripting.h"; sourceTree = ""; }; + 4A5D76A614A9A9CC00DF6C68 /* NSApplication+GitXScripting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSApplication+GitXScripting.m"; sourceTree = ""; }; + 4A5D76A714A9A9CC00DF6C68 /* NSFileHandleExt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSFileHandleExt.h; sourceTree = ""; }; + 4A5D76A814A9A9CC00DF6C68 /* NSFileHandleExt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSFileHandleExt.m; sourceTree = ""; }; + 4A5D76A914A9A9CC00DF6C68 /* NSOutlineViewExt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSOutlineViewExt.h; sourceTree = ""; }; + 4A5D76AA14A9A9CC00DF6C68 /* NSOutlineViewExt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSOutlineViewExt.m; sourceTree = ""; }; + 4A5D76AD14A9A9CC00DF6C68 /* NSString_Truncate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSString_Truncate.h; sourceTree = ""; }; + 4A5D76AE14A9A9CC00DF6C68 /* NSString_Truncate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSString_Truncate.m; sourceTree = ""; }; + 4A5D76AF14A9A9CC00DF6C68 /* PBEasyFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBEasyFS.h; sourceTree = ""; }; + 4A5D76B014A9A9CC00DF6C68 /* PBEasyFS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBEasyFS.m; sourceTree = ""; }; + 4A5D76B114A9A9CC00DF6C68 /* PBEasyPipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBEasyPipe.h; sourceTree = ""; }; + 4A5D76B214A9A9CC00DF6C68 /* PBEasyPipe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBEasyPipe.m; sourceTree = ""; }; + 4A5D76B414A9A9CC00DF6C68 /* GitXTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXTextFieldCell.h; sourceTree = ""; }; + 4A5D76B514A9A9CC00DF6C68 /* GitXTextFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXTextFieldCell.m; sourceTree = ""; }; + 4A5D76B614A9A9CC00DF6C68 /* GLFileView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLFileView.h; sourceTree = ""; }; + 4A5D76B714A9A9CC00DF6C68 /* GLFileView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GLFileView.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D76B814A9A9CC00DF6C68 /* PBAddRemoteSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBAddRemoteSheet.h; sourceTree = ""; }; + 4A5D76B914A9A9CC00DF6C68 /* PBAddRemoteSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBAddRemoteSheet.m; sourceTree = ""; }; + 4A5D76BA14A9A9CC00DF6C68 /* PBCloneRepositoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCloneRepositoryPanel.h; sourceTree = ""; }; + 4A5D76BB14A9A9CC00DF6C68 /* PBCloneRepositoryPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCloneRepositoryPanel.m; sourceTree = ""; }; + 4A5D76C014A9A9CC00DF6C68 /* PBCommitHookFailedSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCommitHookFailedSheet.h; sourceTree = ""; }; + 4A5D76C114A9A9CC00DF6C68 /* PBCommitHookFailedSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCommitHookFailedSheet.m; sourceTree = ""; }; + 4A5D76C214A9A9CC00DF6C68 /* PBCommitMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCommitMessageView.h; sourceTree = ""; }; + 4A5D76C314A9A9CC00DF6C68 /* PBCommitMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCommitMessageView.m; sourceTree = ""; }; + 4A5D76C414A9A9CC00DF6C68 /* PBCreateBranchSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCreateBranchSheet.h; sourceTree = ""; }; + 4A5D76C514A9A9CC00DF6C68 /* PBCreateBranchSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCreateBranchSheet.m; sourceTree = ""; }; + 4A5D76C614A9A9CC00DF6C68 /* PBCreateTagSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCreateTagSheet.h; sourceTree = ""; }; + 4A5D76C714A9A9CC00DF6C68 /* PBCreateTagSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCreateTagSheet.m; sourceTree = ""; }; + 4A5D76C814A9A9CC00DF6C68 /* PBFileChangesTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBFileChangesTableView.h; sourceTree = ""; }; + 4A5D76C914A9A9CC00DF6C68 /* PBFileChangesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBFileChangesTableView.m; sourceTree = ""; }; + 4A5D76CA14A9A9CC00DF6C68 /* PBGitGradientBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGradientBarView.h; sourceTree = ""; }; + 4A5D76CB14A9A9CC00DF6C68 /* PBGitGradientBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitGradientBarView.m; sourceTree = ""; }; + 4A5D76CC14A9A9CC00DF6C68 /* PBGitRevisionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevisionCell.h; sourceTree = ""; }; + 4A5D76CD14A9A9CC00DF6C68 /* PBGitRevisionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitRevisionCell.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D76CE14A9A9CC00DF6C68 /* PBGitXMessageSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitXMessageSheet.h; sourceTree = ""; }; + 4A5D76CF14A9A9CC00DF6C68 /* PBGitXMessageSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitXMessageSheet.m; sourceTree = ""; }; + 4A5D76D014A9A9CC00DF6C68 /* PBIconAndTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBIconAndTextCell.h; sourceTree = ""; }; + 4A5D76D114A9A9CC00DF6C68 /* PBIconAndTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBIconAndTextCell.m; sourceTree = ""; }; + 4A5D76D414A9A9CC00DF6C68 /* PBQLOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBQLOutlineView.h; sourceTree = ""; }; + 4A5D76D514A9A9CC00DF6C68 /* PBQLOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBQLOutlineView.m; sourceTree = ""; }; + 4A5D76D614A9A9CC00DF6C68 /* PBQLTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBQLTextView.h; sourceTree = ""; }; + 4A5D76D714A9A9CC00DF6C68 /* PBQLTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBQLTextView.m; sourceTree = ""; }; + 4A5D76D814A9A9CC00DF6C68 /* PBRefMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRefMenuItem.h; sourceTree = ""; }; + 4A5D76D914A9A9CC00DF6C68 /* PBRefMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRefMenuItem.m; sourceTree = ""; }; + 4A5D76DA14A9A9CC00DF6C68 /* PBRemoteProgressSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRemoteProgressSheet.h; sourceTree = ""; }; + 4A5D76DB14A9A9CC00DF6C68 /* PBRemoteProgressSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBRemoteProgressSheet.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4A5D76DC14A9A9CC00DF6C68 /* PBSourceViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBSourceViewCell.h; sourceTree = ""; }; + 4A5D76DD14A9A9CC00DF6C68 /* PBSourceViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBSourceViewCell.m; sourceTree = ""; }; + 4A5D76DE14A9A9CC00DF6C68 /* PBSourceViewItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBSourceViewItem.h; sourceTree = ""; }; + 4A5D76DF14A9A9CC00DF6C68 /* PBSourceViewItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBSourceViewItem.m; sourceTree = ""; }; + 4A5D776C14A9AEB000DF6C68 /* PBSourceViewAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBSourceViewAction.h; sourceTree = ""; }; + 4A5D776D14A9AEB000DF6C68 /* PBSourceViewAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBSourceViewAction.m; sourceTree = ""; }; + 4A5D776E14A9AEB000DF6C68 /* PBSourceViewBadge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBSourceViewBadge.h; sourceTree = ""; }; + 4A5D776F14A9AEB000DF6C68 /* PBSourceViewBadge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBSourceViewBadge.m; sourceTree = ""; }; + 4A5D777014A9AEB000DF6C68 /* PBSourceViewItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBSourceViewItems.h; sourceTree = ""; }; + 4A5D777114A9AEB000DF6C68 /* PBSourceViewRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBSourceViewRemote.h; sourceTree = ""; }; + 4A5D777214A9AEB000DF6C68 /* PBSourceViewRemote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBSourceViewRemote.m; sourceTree = ""; }; + 4A90A72914A9C12F00D0DA02 /* GitX_Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GitX_Prefix.pch; sourceTree = ""; }; + 4A90A72B14A9C12F00D0DA02 /* README.markdown */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.markdown; sourceTree = SOURCE_ROOT; }; + 4A90A73314A9D1E800D0DA02 /* GitX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GitX.h; path = ../Resources/GitX.h; sourceTree = ""; }; + 4A90A73414A9D24300D0DA02 /* GitX.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = GitX.sdef; 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 = ""; }; + 4D3FB3E5198AEAAF000B4A58 /* PBGitRepositoryDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRepositoryDocument.h; sourceTree = ""; }; + 4D3FB3E6198AEAAF000B4A58 /* PBGitRepositoryDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PBGitRepositoryDocument.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4D6E4F781E56851A004C3A6F /* PBMacros.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBMacros.m; sourceTree = ""; }; + 4D6E4F791E56851A004C3A6F /* PBMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBMacros.h; sourceTree = ""; }; + 4D843B1D1E5D27C3004C3A6F /* PBTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBTask.h; sourceTree = ""; }; + 4D843B1E1E5D27C3004C3A6F /* PBTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBTask.m; sourceTree = ""; }; + 4D843B3E1E5E3939004C3A6F /* PBGitRepository_PBGitBinarySupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRepository_PBGitBinarySupport.h; sourceTree = ""; }; + 4D843B3F1E5E3939004C3A6F /* PBGitRepository_PBGitBinarySupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRepository_PBGitBinarySupport.m; sourceTree = ""; }; + 4DC1853818E6F93200E8DB8F /* Info-gitx.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-gitx.plist"; path = "../Resources/Info-gitx.plist"; sourceTree = ""; }; + 4DECFBB01E662962004C3A6F /* ObjectiveGit+PBCategories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ObjectiveGit+PBCategories.h"; sourceTree = ""; }; + 4DECFBB11E662962004C3A6F /* ObjectiveGit+PBCategories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ObjectiveGit+PBCategories.m"; sourceTree = ""; }; + 551BF111112F371800265053 /* gitx_askpasswd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gitx_askpasswd; sourceTree = BUILT_PRODUCTS_DIR; }; + 630A132E215EB3A000A14FA3 /* libssl.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.1.0.0.dylib; path = /usr/local/Cellar/openssl/1.0.2p/lib/libssl.1.0.0.dylib; sourceTree = ""; }; + 630A132F215EB3A000A14FA3 /* libcrypto.1.0.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.1.0.0.dylib; path = /usr/local/Cellar/openssl/1.0.2p/lib/libcrypto.1.0.0.dylib; sourceTree = ""; }; + 63663D73215D408300FBAA0A /* ObjectiveGit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjectiveGit.framework; path = Carthage/Build/Mac/ObjectiveGit.framework; sourceTree = ""; }; + 638CE7582163BF7B00A2CFA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = ../Base.lproj/MainMenu.xib; sourceTree = ""; }; + 638CE7592163BF7B00A2CFA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = ../Base.lproj/PBAddRemoteSheet.xib; sourceTree = ""; }; + 638CE75A2163BF7B00A2CFA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = ../Base.lproj/PBCloneRepositoryPanel.xib; sourceTree = ""; }; + 638CE75B2163BF7C00A2CFA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = ../Base.lproj/PBCreateBranchSheet.xib; sourceTree = ""; }; + 638CE75C2163BF7C00A2CFA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = ../Base.lproj/PBCreateTagSheet.xib; sourceTree = ""; }; + 638CE75D2163BF7C00A2CFA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = ../Base.lproj/PBRemoteProgressSheet.xib; sourceTree = ""; }; + 638CE75E2163BF7C00A2CFA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = ../Base.lproj/RepositoryWindow.xib; sourceTree = ""; }; + 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 = ""; }; + 6C2581091C2720E60080A89A /* GitXCommitCopier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXCommitCopier.h; sourceTree = ""; }; + 6C25810A1C2720E60080A89A /* GitXCommitCopier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXCommitCopier.m; sourceTree = ""; }; + 6C4855C61D57DCFC0027A7B4 /* PBTerminalUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBTerminalUtil.h; sourceTree = ""; }; + 6C4855C71D57DCFC0027A7B4 /* PBTerminalUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBTerminalUtil.m; sourceTree = ""; }; + 6C4855D81D57E4FA0027A7B4 /* PBOpenShallowRepositoryErrorRecoveryAttempter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBOpenShallowRepositoryErrorRecoveryAttempter.h; sourceTree = ""; }; + 6C4855D91D57E4FA0027A7B4 /* PBOpenShallowRepositoryErrorRecoveryAttempter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBOpenShallowRepositoryErrorRecoveryAttempter.m; sourceTree = ""; }; + 6C5244021E00C33F0051DE20 /* PBHistorySearchMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBHistorySearchMode.h; sourceTree = ""; }; + 6C5244121E00C66E0051DE20 /* PBHistorySearchMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBHistorySearchMode.m; sourceTree = ""; }; 77C82804067257F0000B614F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; - 77C8280B06725ACE000B614F /* ApplicationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplicationController.h; sourceTree = ""; }; - 77C8280C06725ACE000B614F /* ApplicationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ApplicationController.m; sourceTree = ""; }; - 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* GitX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GitX.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 911111E10E58BD5A00BF76B4 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/RepositoryWindow.xib; sourceTree = ""; }; - 911111F60E594F3F00BF76B4 /* PBRepositoryDocumentController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRepositoryDocumentController.h; sourceTree = ""; }; - 911111F70E594F3F00BF76B4 /* PBRepositoryDocumentController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRepositoryDocumentController.m; sourceTree = ""; }; 911112360E5A097800BF76B4 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; - 913D5E440E55640C00CECEA2 /* gitx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = gitx.m; sourceTree = ""; }; 913D5E490E55644600CECEA2 /* gitx */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gitx; sourceTree = BUILT_PRODUCTS_DIR; }; - 913D5E5D0E556A9300CECEA2 /* PBCLIProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCLIProxy.h; sourceTree = ""; }; - 913D5E5E0E556A9300CECEA2 /* PBCLIProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCLIProxy.m; sourceTree = ""; }; - 91B103CA0E898EC300C84364 /* PBIconAndTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBIconAndTextCell.h; sourceTree = ""; }; - 91B103CB0E898EC300C84364 /* PBIconAndTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBIconAndTextCell.m; sourceTree = ""; }; - 93CB42C00EAB7B2200530609 /* PBGitDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitDefaults.h; sourceTree = ""; }; - 93CB42C10EAB7B2200530609 /* PBGitDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitDefaults.m; sourceTree = ""; }; - 93F7857D0EA3ABF100C1F443 /* PBCommitMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCommitMessageView.h; sourceTree = ""; }; - 93F7857E0EA3ABF100C1F443 /* PBCommitMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCommitMessageView.m; sourceTree = ""; }; - 93FCCBA80EA8AF450061B02B /* PBGitConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitConfig.m; sourceTree = ""; }; - D26DC6440E782C9000C777B2 /* gitx.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = gitx.icns; sourceTree = ""; }; - EB2A73480FEE3F09006601CF /* PBCollapsibleSplitView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCollapsibleSplitView.h; sourceTree = ""; }; - EB2A73490FEE3F09006601CF /* PBCollapsibleSplitView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCollapsibleSplitView.m; sourceTree = ""; }; - F50A411D0EBB874C00208746 /* mainSplitterBar.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = mainSplitterBar.tiff; path = Images/mainSplitterBar.tiff; sourceTree = ""; }; - F50A411E0EBB874C00208746 /* mainSplitterDimple.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = mainSplitterDimple.tiff; path = Images/mainSplitterDimple.tiff; sourceTree = ""; }; - F50A41210EBB875D00208746 /* PBNiceSplitView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBNiceSplitView.h; sourceTree = ""; }; - F50A41220EBB875D00208746 /* PBNiceSplitView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBNiceSplitView.m; sourceTree = ""; }; - F50FE0E10E07BE9600854FCD /* PBGitRevisionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevisionCell.h; sourceTree = ""; }; - F50FE0E20E07BE9600854FCD /* PBGitRevisionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRevisionCell.m; sourceTree = ""; }; - F51308590E0740F2000C8BCD /* PBQLOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBQLOutlineView.h; sourceTree = ""; }; - F513085A0E0740F2000C8BCD /* PBQLOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBQLOutlineView.m; sourceTree = ""; }; - F5140DC70E8A8EB20091E9F3 /* RoundedRectangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoundedRectangle.h; sourceTree = ""; }; - F5140DC80E8A8EB20091E9F3 /* RoundedRectangle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoundedRectangle.m; sourceTree = ""; }; - F523CEB40ED3399100DDD714 /* PBGitIndexController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitIndexController.h; sourceTree = ""; }; - F523CEB50ED3399200DDD714 /* PBGitIndexController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitIndexController.m; sourceTree = ""; }; - F52BCE020E84208300AA3741 /* PBGitHistoryView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PBGitHistoryView.xib; sourceTree = ""; }; - F52BCE050E84211300AA3741 /* PBGitHistoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitHistoryController.h; sourceTree = ""; }; - F52BCE060E84211300AA3741 /* PBGitHistoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitHistoryController.m; sourceTree = ""; }; - F53C4DF50E97FC630022AD59 /* PBGitBinary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitBinary.h; sourceTree = ""; }; - F53C4DF60E97FC630022AD59 /* PBGitBinary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitBinary.m; sourceTree = ""; }; - F53EE3590E06BBA00022B925 /* CWQuickLook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CWQuickLook.h; sourceTree = ""; }; - F53FF2030E7ABB5300389171 /* PBGitRevSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevSpecifier.h; sourceTree = ""; }; - F53FF2040E7ABB5300389171 /* PBGitRevSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRevSpecifier.m; sourceTree = ""; }; - F56174550E058893001DCD79 /* PBGitTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitTree.h; sourceTree = ""; }; - F56174560E058893001DCD79 /* PBGitTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitTree.m; sourceTree = ""; }; - F56244070E9684B0002B6C44 /* PBUnsortableTableHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBUnsortableTableHeader.h; sourceTree = ""; }; - F56244080E9684B0002B6C44 /* PBUnsortableTableHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBUnsortableTableHeader.m; sourceTree = ""; }; - F562C8850FE1766C000EC528 /* NSString_RegEx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSString_RegEx.h; sourceTree = ""; }; - F562C8860FE1766C000EC528 /* NSString_RegEx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSString_RegEx.m; sourceTree = ""; }; - F56524B90E02D22D00F03B52 /* NSFileHandleExt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSFileHandleExt.m; sourceTree = ""; }; - F56524BA0E02D22D00F03B52 /* NSFileHandleExt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSFileHandleExt.h; sourceTree = ""; }; - F56524EE0E02D45200F03B52 /* PBGitCommit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitCommit.h; sourceTree = ""; }; - F56524EF0E02D45200F03B52 /* PBGitCommit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitCommit.m; sourceTree = ""; }; + 978357A4189C05B100ADC689 /* FolderClosedTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = FolderClosedTemplate.pdf; sourceTree = ""; }; + 978357A5189C05B100ADC689 /* FolderTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = FolderTemplate.pdf; sourceTree = ""; }; + 978357A8189C074500ADC689 /* BranchTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = BranchTemplate.pdf; sourceTree = ""; }; + 978357A9189C074500ADC689 /* RemoteBranchTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = RemoteBranchTemplate.pdf; sourceTree = ""; }; + 978357AA189C074500ADC689 /* RemoteTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = RemoteTemplate.pdf; sourceTree = ""; }; + 978357AB189C074500ADC689 /* StageTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = StageTemplate.pdf; sourceTree = ""; }; + 978357AC189C074500ADC689 /* TagTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = TagTemplate.pdf; sourceTree = ""; }; + 978357B2189C19A000ADC689 /* AddBranchTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = AddBranchTemplate.pdf; sourceTree = ""; }; + 978357B3189C19A000ADC689 /* AddLabelTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = AddLabelTemplate.pdf; sourceTree = ""; }; + 978357B4189C19A000ADC689 /* AddRemoteTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = AddRemoteTemplate.pdf; sourceTree = ""; }; + 978357B5189C19A000ADC689 /* FetchTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = FetchTemplate.pdf; sourceTree = ""; }; + 978357B6189C19A000ADC689 /* PullTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = PullTemplate.pdf; sourceTree = ""; }; + 978357B7189C19A000ADC689 /* PushTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = PushTemplate.pdf; sourceTree = ""; }; + 978357BE189C1F5700ADC689 /* CherryPickTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = CherryPickTemplate.pdf; sourceTree = ""; }; + 978357BF189C1F5700ADC689 /* MergeTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = MergeTemplate.pdf; sourceTree = ""; }; + 978357C0189C1F5700ADC689 /* RebaseTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = RebaseTemplate.pdf; sourceTree = ""; }; + 978357C4189C238300ADC689 /* DetailViewTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = DetailViewTemplate.pdf; sourceTree = ""; }; + 978357C6189C23AF00ADC689 /* TreeViewTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = TreeViewTemplate.pdf; sourceTree = ""; }; + 97CF01F618A6C5BB00E30F2B /* deleted_file.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = deleted_file.pdf; sourceTree = ""; }; + 97CF01F718A6C5BB00E30F2B /* empty_file.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = empty_file.pdf; sourceTree = ""; }; + 97CF01F818A6C5BB00E30F2B /* new_file.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = new_file.pdf; sourceTree = ""; }; + A2F8D0DD17AAB32500580B84 /* PBGitStash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitStash.h; sourceTree = ""; }; + A2F8D0DE17AAB32500580B84 /* PBGitStash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitStash.m; sourceTree = ""; }; + A2F8D0E917AAB95E00580B84 /* PBGitSVStashItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVStashItem.h; sourceTree = ""; }; + A2F8D0EA17AAB95E00580B84 /* PBGitSVStashItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVStashItem.m; sourceTree = ""; }; + BF42F1321E025403004769FF /* GitXTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXTextView.m; sourceTree = ""; }; + BF42F1431E025411004769FF /* GitXTextView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GitXTextView.h; sourceTree = ""; }; + BFFCB5461E3EFA9F00E0B254 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D87127001229A21C00012334 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = System/Library/Frameworks/ScriptingBridge.framework; sourceTree = SDKROOT; }; + D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = /System/Library/Frameworks/ScriptingBridge.framework; sourceTree = ""; }; F56526230E03D85900F03B52 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = /System/Library/Frameworks/WebKit.framework; sourceTree = ""; }; - F56526290E03D89B00F03B52 /* PBWebHistoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebHistoryController.h; sourceTree = ""; }; - F565262A0E03D89B00F03B52 /* PBWebHistoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebHistoryController.m; sourceTree = ""; }; - F567CC39106E6B910059BB9D /* GitXTesting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GitXTesting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F567CC3A106E6B910059BB9D /* GitXTesting-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GitXTesting-Info.plist"; sourceTree = ""; }; - F569AE920F2CBD7C00C2FFA7 /* Credits.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = Credits.html; sourceTree = ""; }; - F56ADDD70ED19F9E002AC78F /* AddBranchTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = AddBranchTemplate.png; path = Images/AddBranchTemplate.png; sourceTree = ""; }; - F56ADDD80ED19F9E002AC78F /* AddLabelTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = AddLabelTemplate.png; path = Images/AddLabelTemplate.png; sourceTree = ""; }; - F56CC7270E65E0AD004307B4 /* PBGitGraphLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGraphLine.h; sourceTree = ""; }; - F56CC7300E65E0E5004307B4 /* PBGraphCellInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGraphCellInfo.h; sourceTree = ""; }; - F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGraphCellInfo.m; sourceTree = ""; }; - F57240BA0E9678EA00D8EE66 /* deleted_file.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = deleted_file.png; path = Images/deleted_file.png; sourceTree = ""; }; - F574A2830EAE2EAC003F2CB1 /* PBRefController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRefController.h; sourceTree = ""; }; - F574A2840EAE2EAC003F2CB1 /* PBRefController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRefController.m; sourceTree = ""; }; - F57CC38F0E05DDF2000472E2 /* PBEasyPipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBEasyPipe.h; sourceTree = ""; }; - F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBEasyPipe.m; sourceTree = ""; }; - F57CC43F0E05E496000472E2 /* PBGitWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitWindowController.h; sourceTree = ""; }; - F57CC4400E05E496000472E2 /* PBGitWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitWindowController.m; sourceTree = ""; }; - F580E6AD0E733276009E2D3F /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = ""; }; - F5886A0A0ED5D27A0066E74C /* speedtest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = speedtest.m; sourceTree = ""; }; - F5886A100ED5D33D0066E74C /* SpeedTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpeedTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; - F5886A120ED5D33D0066E74C /* SpeedTest-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SpeedTest-Info.plist"; sourceTree = ""; }; - F59116E50E843BB50072CCB1 /* PBGitCommitView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PBGitCommitView.xib; sourceTree = ""; }; - F59116E70E843BCB0072CCB1 /* PBGitCommitController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitCommitController.h; sourceTree = ""; }; - F59116E80E843BCB0072CCB1 /* PBGitCommitController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitCommitController.m; sourceTree = ""; }; - F593DF760E9E636C003A8559 /* PBFileChangesTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBFileChangesTableView.h; sourceTree = ""; }; - F593DF770E9E636C003A8559 /* PBFileChangesTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBFileChangesTableView.m; sourceTree = ""; }; - F5945E150E02B0C200706420 /* PBGitRepository.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRepository.h; sourceTree = ""; }; - F5945E160E02B0C200706420 /* PBGitRepository.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRepository.m; sourceTree = ""; }; - F59F1DD3105C4FF300115F88 /* PBGitIndex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitIndex.h; sourceTree = ""; }; - F59F1DD4105C4FF300115F88 /* PBGitIndex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitIndex.m; sourceTree = ""; }; - F5AD56770E79B78100EDAAFE /* PBCommitList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCommitList.h; sourceTree = ""; }; - F5AD56780E79B78100EDAAFE /* PBCommitList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCommitList.m; sourceTree = ""; }; - F5B721C30E05CF7E00AF29DC /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; - F5C007730E731B48007B84B2 /* PBGitRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRef.h; sourceTree = ""; }; - F5C007740E731B48007B84B2 /* PBGitRef.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRef.m; sourceTree = ""; }; - F5C580E40EDA250900995434 /* libgit2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgit2.a; path = libgit2/libgit2.a; sourceTree = ""; }; - F5C6F68B0E65FF9300478D97 /* PBGitLane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitLane.h; sourceTree = ""; }; - F5C6F68C0E65FF9300478D97 /* PBGitLane.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PBGitLane.mm; sourceTree = ""; }; - F5D2DC850EA401A80034AD24 /* PBGitConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitConfig.h; sourceTree = ""; }; F5D619ED0EAE62EA00341D73 /* html */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = folder; path = html; sourceTree = ""; }; - F5DFFA6A0E075D8800617813 /* PBEasyFS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBEasyFS.h; sourceTree = ""; }; - F5DFFA6B0E075D8800617813 /* PBEasyFS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBEasyFS.m; sourceTree = ""; }; - F5E424100EA3E4D60046E362 /* PBDiffWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PBDiffWindow.xib; sourceTree = ""; }; - F5E424130EA3E4E10046E362 /* PBDiffWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBDiffWindowController.m; sourceTree = ""; }; - F5E424140EA3E4E10046E362 /* PBDiffWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBDiffWindowController.h; sourceTree = ""; }; - F5E424160EA3E4EB0046E362 /* PBWebDiffController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebDiffController.m; sourceTree = ""; }; - F5E424170EA3E4EB0046E362 /* PBWebDiffController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebDiffController.h; sourceTree = ""; }; F5E4DBFA0EAB58D90013FAFC /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; - F5E926040E8827D300056E75 /* PBViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBViewController.h; sourceTree = ""; }; - F5E926050E8827D300056E75 /* PBViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBViewController.m; sourceTree = ""; }; - F5E927F60E883E7200056E75 /* PBChangedFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBChangedFile.h; sourceTree = ""; }; - F5E927F70E883E7200056E75 /* PBChangedFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBChangedFile.m; sourceTree = ""; }; - F5E927FA0E883F0700056E75 /* PBWebChangesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebChangesController.h; sourceTree = ""; }; - F5E927FB0E883F0700056E75 /* PBWebChangesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebChangesController.m; sourceTree = ""; }; - F5E92A1A0E88550E00056E75 /* empty_file.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = empty_file.png; path = Images/empty_file.png; sourceTree = ""; }; - F5E92A220E88569500056E75 /* new_file.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = new_file.png; path = Images/new_file.png; sourceTree = ""; }; - F5EF8C8C0E9D4A5D0050906B /* PBWebController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBWebController.h; sourceTree = ""; }; - F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBWebController.m; sourceTree = ""; }; - F5FC41F20EBCBD4300191D80 /* PBGitXProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitXProtocol.h; sourceTree = ""; }; - F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitXProtocol.m; sourceTree = ""; }; - F5FC43C30EBD050800191D80 /* PBRefContextDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRefContextDelegate.h; sourceTree = ""; }; - F5FC43FC0EBD08EE00191D80 /* PBRefMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRefMenuItem.h; sourceTree = ""; }; - F5FC43FD0EBD08EE00191D80 /* PBRefMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRefMenuItem.m; sourceTree = ""; }; - F5FE6C010EB13BC900F30D12 /* PBServicesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBServicesController.h; sourceTree = ""; }; - F5FE6C020EB13BC900F30D12 /* PBServicesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBServicesController.m; sourceTree = ""; }; - F5FF4E160E0829C20006317A /* PBGitRevList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevList.h; sourceTree = ""; }; - F5FF4E170E0829C20006317A /* PBGitRevList.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PBGitRevList.mm; sourceTree = ""; }; - F5FF4E780E082E440006317A /* PBGitGrapher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGrapher.h; sourceTree = ""; }; - F5FF4E790E082E440006317A /* PBGitGrapher.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PBGitGrapher.mm; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 8D11072E0486CEB800E47090 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, - F56526240E03D85900F03B52 /* WebKit.framework in Frameworks */, - 911112370E5A097800BF76B4 /* Security.framework in Frameworks */, - F580E6AE0E733276009E2D3F /* Sparkle.framework in Frameworks */, - F5E4DBFB0EAB58D90013FAFC /* SystemConfiguration.framework in Frameworks */, - F5C580E50EDA250900995434 /* libgit2.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 913D5E470E55644600CECEA2 /* Frameworks */ = { + 551BF10F112F371800265053 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4AC42F7D16BFBADA007CCA3A /* AppKit.framework in Frameworks */, + 4A1F4E6917AFE969004D51E9 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - F567CC37106E6B910059BB9D /* Frameworks */ = { + 8D11072E0486CEB800E47090 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F567CC7F106E6C470059BB9D /* libgit2.a in Frameworks */, + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + F56526240E03D85900F03B52 /* WebKit.framework in Frameworks */, + 911112370E5A097800BF76B4 /* Security.framework in Frameworks */, + F5E4DBFB0EAB58D90013FAFC /* SystemConfiguration.framework in Frameworks */, + 63663D74215D408300FBAA0A /* ObjectiveGit.framework in Frameworks */, + D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */, + D87127011229A21C00012334 /* QuartzCore.framework in Frameworks */, + 37A5657D1EAD096300CA0332 /* Quartz.framework in Frameworks */, + 0A6858C711F7EA8A00AC2BE4 /* CoreServices.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - F5886A0E0ED5D33D0066E74C /* Frameworks */ = { + 913D5E470E55644600CECEA2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F5C580F50EDA251100995434 /* libgit2.a in Frameworks */, - F5886A1E0ED5D3880066E74C /* Cocoa.framework in Frameworks */, + 4A40159914067B7A00DB9C07 /* AppKit.framework in Frameworks */, + 63663D78215D42AE00FBAA0A /* ObjectiveGit.framework in Frameworks */, + 4A40159714067B6300DB9C07 /* CoreFoundation.framework in Frameworks */, + D89E9B141218BA260097A90B /* ScriptingBridge.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 080E96DDFE201D6D7F000001 /* Classes */ = { - isa = PBXGroup; - children = ( - ); - name = Classes; - sourceTree = ""; - }; - 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { - isa = PBXGroup; - children = ( - F5E4DBFA0EAB58D90013FAFC /* SystemConfiguration.framework */, - F580E6AD0E733276009E2D3F /* Sparkle.framework */, - F56526230E03D85900F03B52 /* WebKit.framework */, - 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, - ); - name = "Linked Frameworks"; - sourceTree = ""; - }; - 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { - isa = PBXGroup; - children = ( - 911112360E5A097800BF76B4 /* Security.framework */, - 77C82804067257F0000B614F /* CoreData.framework */, - 29B97325FDCFA39411CA2CEA /* Foundation.framework */, - 29B97324FDCFA39411CA2CEA /* AppKit.framework */, - ); - name = "Other Frameworks"; - sourceTree = ""; - }; 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( 8D1107320486CEB800E47090 /* GitX.app */, 913D5E490E55644600CECEA2 /* gitx */, - F5886A100ED5D33D0066E74C /* SpeedTest.app */, - F567CC39106E6B910059BB9D /* GitXTesting.framework */, + 551BF111112F371800265053 /* gitx_askpasswd */, ); name = Products; sourceTree = ""; @@ -396,324 +545,404 @@ 29B97314FDCFA39411CA2CEA /* GitTest */ = { isa = PBXGroup; children = ( - F5886A080ED5D26B0066E74C /* SpeedTest */, - 913D5E420E5563FD00CECEA2 /* cli */, - F57CC43E0E05E472000472E2 /* Aux */, - F57CC3850E05DDC1000472E2 /* Controllers */, - F56174540E05887E001DCD79 /* Git */, - F5D619ED0EAE62EA00341D73 /* html */, - 7756732906782D8800D1FEB8 /* Models */, - 080E96DDFE201D6D7F000001 /* Classes */, - 29B97315FDCFA39411CA2CEA /* Other Sources */, - 29B97317FDCFA39411CA2CEA /* Resources */, + 4A90A72B14A9C12F00D0DA02 /* README.markdown */, + 4A5D762114A9A9CC00DF6C68 /* Classes */, + 4A5D757C14A9A90500DF6C68 /* Resources */, + 37A5656C1EAD077400CA0332 /* cli tool */, + 37A5656D1EAD07C200CA0332 /* gitx_askpasswd */, 29B97323FDCFA39411CA2CEA /* Frameworks */, + 630A132F215EB3A000A14FA3 /* libcrypto.1.0.0.dylib */, + 630A132E215EB3A000A14FA3 /* libssl.1.0.0.dylib */, 19C28FACFE9D520D11CA2CBB /* Products */, - F5886A120ED5D33D0066E74C /* SpeedTest-Info.plist */, - F567CC3A106E6B910059BB9D /* GitXTesting-Info.plist */, ); name = GitTest; sourceTree = ""; - }; - 29B97315FDCFA39411CA2CEA /* Other Sources */ = { - isa = PBXGroup; - children = ( - 3BC07F4A0ED5A5C5009A7768 /* HistoryViewTemplate.png */, - 3BC07F4B0ED5A5C5009A7768 /* CommitViewTemplate.png */, - F56ADDD70ED19F9E002AC78F /* AddBranchTemplate.png */, - F56ADDD80ED19F9E002AC78F /* AddLabelTemplate.png */, - 056438B60ED0C40B00985397 /* DetailViewTemplate.png */, - F57240BA0E9678EA00D8EE66 /* deleted_file.png */, - F5E92A1A0E88550E00056E75 /* empty_file.png */, - 32CA4F630368D1EE00C91783 /* GitX_Prefix.pch */, - 29B97316FDCFA39411CA2CEA /* main.m */, - F5E92A220E88569500056E75 /* new_file.png */, - ); - name = "Other Sources"; - sourceTree = ""; - }; - 29B97317FDCFA39411CA2CEA /* Resources */ = { - isa = PBXGroup; - children = ( - F50A41130EBB872D00208746 /* Widgets */, - 47DBDB920E94F47200671A1E /* Preference Icons */, - D26DC6440E782C9000C777B2 /* gitx.icns */, - 8D1107310486CEB800E47090 /* Info.plist */, - 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, - F5B721C20E05CF7E00AF29DC /* MainMenu.xib */, - 911111E00E58BD5A00BF76B4 /* RepositoryWindow.xib */, - F5E424100EA3E4D60046E362 /* PBDiffWindow.xib */, - F52BCE020E84208300AA3741 /* PBGitHistoryView.xib */, - F59116E50E843BB50072CCB1 /* PBGitCommitView.xib */, - 47DBDB680E94EF6500671A1E /* Preferences.xib */, - F569AE920F2CBD7C00C2FFA7 /* Credits.html */, - ); - name = Resources; - sourceTree = ""; + usesTabs = 1; }; 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( - F5C580E40EDA250900995434 /* libgit2.a */, - 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, - 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + 63663D73215D408300FBAA0A /* ObjectiveGit.framework */, + 37A5657C1EAD096300CA0332 /* Quartz.framework */, + 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + 4A40159814067B7A00DB9C07 /* AppKit.framework */, + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, + 77C82804067257F0000B614F /* CoreData.framework */, + 4A40159614067B6300DB9C07 /* CoreFoundation.framework */, + 0A6858C611F7EA8A00AC2BE4 /* CoreServices.framework */, + 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + D87127001229A21C00012334 /* QuartzCore.framework */, + D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */, + D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */, + 911112360E5A097800BF76B4 /* Security.framework */, + F5E4DBFA0EAB58D90013FAFC /* SystemConfiguration.framework */, + F56526230E03D85900F03B52 /* WebKit.framework */, ); name = Frameworks; sourceTree = ""; }; - 47DBDB920E94F47200671A1E /* Preference Icons */ = { - isa = PBXGroup; - children = ( - 47DBDBB00E94F6CA00671A1E /* Updates.png */, - ); - name = "Preference Icons"; - sourceTree = ""; - }; - 7756732906782D8800D1FEB8 /* Models */ = { - isa = PBXGroup; - children = ( - 770B37EC0679A11B001EADE2 /* GitTest_DataModel.xcdatamodel */, - ); - name = Models; - sourceTree = ""; - }; - 913D5E420E5563FD00CECEA2 /* cli */ = { - isa = PBXGroup; - children = ( - 913D5E5D0E556A9300CECEA2 /* PBCLIProxy.h */, - 913D5E5E0E556A9300CECEA2 /* PBCLIProxy.m */, - 913D5E440E55640C00CECEA2 /* gitx.m */, - ); - name = cli; - sourceTree = ""; - }; - F50A41130EBB872D00208746 /* Widgets */ = { + 37A5656C1EAD077400CA0332 /* cli tool */ = { isa = PBXGroup; children = ( - F50A411D0EBB874C00208746 /* mainSplitterBar.tiff */, - F50A411E0EBB874C00208746 /* mainSplitterDimple.tiff */, + 4A5D768614A9A9CC00DF6C68 /* gitx.m */, + 4A90A73314A9D1E800D0DA02 /* GitX.h */, + 4DC1853818E6F93200E8DB8F /* Info-gitx.plist */, ); - name = Widgets; + name = "cli tool"; + path = Classes; sourceTree = ""; }; - F56174540E05887E001DCD79 /* Git */ = { + 37A5656D1EAD07C200CA0332 /* gitx_askpasswd */ = { isa = PBXGroup; children = ( - F59F1DD2105C4FDE00115F88 /* Index */, - F5E927E30E883D6800056E75 /* Commit */, - F5E927E10E883D2E00056E75 /* History */, - F5945E150E02B0C200706420 /* PBGitRepository.h */, - F5945E160E02B0C200706420 /* PBGitRepository.m */, - F53C4DF50E97FC630022AD59 /* PBGitBinary.h */, - F53C4DF60E97FC630022AD59 /* PBGitBinary.m */, - F5D2DC850EA401A80034AD24 /* PBGitConfig.h */, - 93FCCBA80EA8AF450061B02B /* PBGitConfig.m */, + 4A5D768714A9A9CC00DF6C68 /* gitx_askpasswd_main.m */, ); - name = Git; + name = gitx_askpasswd; + path = Classes; sourceTree = ""; }; - F57CC3850E05DDC1000472E2 /* Controllers */ = { + 4A5D757C14A9A90500DF6C68 /* Resources */ = { isa = PBXGroup; children = ( - F5B161BB0EAB6E0C005A1DE1 /* Diff */, - F5EF8C880E9D498F0050906B /* History */, - F5E927F90E883EF600056E75 /* Commit */, - 77C8280B06725ACE000B614F /* ApplicationController.h */, - 77C8280C06725ACE000B614F /* ApplicationController.m */, - 93CB42C00EAB7B2200530609 /* PBGitDefaults.h */, - 93CB42C10EAB7B2200530609 /* PBGitDefaults.m */, - F57CC43F0E05E496000472E2 /* PBGitWindowController.h */, - F57CC4400E05E496000472E2 /* PBGitWindowController.m */, - 911111F60E594F3F00BF76B4 /* PBRepositoryDocumentController.h */, - 911111F70E594F3F00BF76B4 /* PBRepositoryDocumentController.m */, - F5E926040E8827D300056E75 /* PBViewController.h */, - F5E926050E8827D300056E75 /* PBViewController.m */, - F5EF8C8C0E9D4A5D0050906B /* PBWebController.h */, - F5EF8C8D0E9D4A5D0050906B /* PBWebController.m */, - F5FE6C010EB13BC900F30D12 /* PBServicesController.h */, - F5FE6C020EB13BC900F30D12 /* PBServicesController.m */, - 47DBDB560E94EDE700671A1E /* DBPrefsWindowController.h */, - 47DBDB570E94EDE700671A1E /* DBPrefsWindowController.m */, - 47DBDB650E94EE8B00671A1E /* PBPrefsWindowController.h */, - 47DBDB660E94EE8B00671A1E /* PBPrefsWindowController.m */, - ); - name = Controllers; + 4A5D760214A9A99E00DF6C68 /* English.lproj */, + 4A90A73414A9D24300D0DA02 /* GitX.sdef */, + 4A90A72914A9C12F00D0DA02 /* GitX_Prefix.pch */, + F5D619ED0EAE62EA00341D73 /* html */, + 4A5D758114A9A90500DF6C68 /* Images */, + BFFCB5461E3EFA9F00E0B254 /* Images.xcassets */, + 4A5D75B414A9A90500DF6C68 /* Info.plist */, + 4A5D75B514A9A90500DF6C68 /* source.css */, + 4A5D75B714A9A90500DF6C68 /* UpdateKey.pem */, + 4A5D75B914A9A90500DF6C68 /* XIBs */, + ); + path = Resources; sourceTree = ""; }; - F57CC43E0E05E472000472E2 /* Aux */ = { + 4A5D758114A9A90500DF6C68 /* Images */ = { isa = PBXGroup; children = ( - 47DBDBC80E95016F00671A1E /* PBNSURLPathUserDefaultsTransfomer.h */, - 47DBDBC90E95016F00671A1E /* PBNSURLPathUserDefaultsTransfomer.m */, - F5AD56770E79B78100EDAAFE /* PBCommitList.h */, - F5AD56780E79B78100EDAAFE /* PBCommitList.m */, - F56524B90E02D22D00F03B52 /* NSFileHandleExt.m */, - F56524BA0E02D22D00F03B52 /* NSFileHandleExt.h */, - F562C8850FE1766C000EC528 /* NSString_RegEx.h */, - F562C8860FE1766C000EC528 /* NSString_RegEx.m */, - F57CC38F0E05DDF2000472E2 /* PBEasyPipe.h */, - F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */, - F5DFFA6A0E075D8800617813 /* PBEasyFS.h */, - F5DFFA6B0E075D8800617813 /* PBEasyFS.m */, - F53EE3590E06BBA00022B925 /* CWQuickLook.h */, - F51308590E0740F2000C8BCD /* PBQLOutlineView.h */, - F513085A0E0740F2000C8BCD /* PBQLOutlineView.m */, - 91B103CA0E898EC300C84364 /* PBIconAndTextCell.h */, - 91B103CB0E898EC300C84364 /* PBIconAndTextCell.m */, - F5140DC70E8A8EB20091E9F3 /* RoundedRectangle.h */, - F5140DC80E8A8EB20091E9F3 /* RoundedRectangle.m */, - F56244070E9684B0002B6C44 /* PBUnsortableTableHeader.h */, - F56244080E9684B0002B6C44 /* PBUnsortableTableHeader.m */, - F50A41210EBB875D00208746 /* PBNiceSplitView.h */, - F50A41220EBB875D00208746 /* PBNiceSplitView.m */, - EB2A73480FEE3F09006601CF /* PBCollapsibleSplitView.h */, - EB2A73490FEE3F09006601CF /* PBCollapsibleSplitView.m */, - F5FC41F20EBCBD4300191D80 /* PBGitXProtocol.h */, - F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */, - ); - name = Aux; + 97CF01F618A6C5BB00E30F2B /* deleted_file.pdf */, + 97CF01F718A6C5BB00E30F2B /* empty_file.pdf */, + 97CF01F818A6C5BB00E30F2B /* new_file.pdf */, + 978357C6189C23AF00ADC689 /* TreeViewTemplate.pdf */, + 978357B2189C19A000ADC689 /* AddBranchTemplate.pdf */, + 978357B3189C19A000ADC689 /* AddLabelTemplate.pdf */, + 978357B4189C19A000ADC689 /* AddRemoteTemplate.pdf */, + 978357A8189C074500ADC689 /* BranchTemplate.pdf */, + 978357BE189C1F5700ADC689 /* CherryPickTemplate.pdf */, + 978357C4189C238300ADC689 /* DetailViewTemplate.pdf */, + 978357B5189C19A000ADC689 /* FetchTemplate.pdf */, + 978357A4189C05B100ADC689 /* FolderClosedTemplate.pdf */, + 978357A5189C05B100ADC689 /* FolderTemplate.pdf */, + 4A5D759B14A9A90500DF6C68 /* mainSplitterBar.tiff */, + 4A5D759C14A9A90500DF6C68 /* mainSplitterDimple.tiff */, + 978357BF189C1F5700ADC689 /* MergeTemplate.pdf */, + 978357B6189C19A000ADC689 /* PullTemplate.pdf */, + 978357B7189C19A000ADC689 /* PushTemplate.pdf */, + 978357C0189C1F5700ADC689 /* RebaseTemplate.pdf */, + 978357A9189C074500ADC689 /* RemoteBranchTemplate.pdf */, + 978357AA189C074500ADC689 /* RemoteTemplate.pdf */, + 4A5D75AF14A9A90500DF6C68 /* rewindImage.pdf */, + 978357AB189C074500ADC689 /* StageTemplate.pdf */, + 978357AC189C074500ADC689 /* TagTemplate.pdf */, + ); + path = Images; sourceTree = ""; }; - F5886A080ED5D26B0066E74C /* SpeedTest */ = { + 4A5D75B914A9A90500DF6C68 /* XIBs */ = { isa = PBXGroup; children = ( - F5886A0A0ED5D27A0066E74C /* speedtest.m */, - ); - name = SpeedTest; + 4A5D75BA14A9A90500DF6C68 /* OpenRecentPopup.xib */, + 4A5D75BB14A9A90500DF6C68 /* PBCommitHookFailedSheet.xib */, + 4A5D75BC14A9A90500DF6C68 /* PBDiffWindow.xib */, + 4A5D75BD14A9A90500DF6C68 /* PBGitCommitView.xib */, + 4A5D75BE14A9A90500DF6C68 /* PBGitHistoryView.xib */, + 4A5D75BF14A9A90500DF6C68 /* PBGitSidebarView.xib */, + 4A5D75C014A9A90500DF6C68 /* PBGitXMessageSheet.xib */, + ); + path = XIBs; sourceTree = ""; }; - F59F1DD2105C4FDE00115F88 /* Index */ = { + 4A5D760214A9A99E00DF6C68 /* English.lproj */ = { isa = PBXGroup; children = ( - F5E927F60E883E7200056E75 /* PBChangedFile.h */, - F5E927F70E883E7200056E75 /* PBChangedFile.m */, - F59F1DD3105C4FF300115F88 /* PBGitIndex.h */, - F59F1DD4105C4FF300115F88 /* PBGitIndex.m */, - ); - name = Index; + 4A5D760314A9A99E00DF6C68 /* InfoPlist.strings */, + 4A5D760514A9A99E00DF6C68 /* MainMenu.xib */, + 4A5D760714A9A99E00DF6C68 /* PBAddRemoteSheet.xib */, + 4A5D760914A9A99E00DF6C68 /* PBCloneRepositoryPanel.xib */, + 4A5D760D14A9A99E00DF6C68 /* PBCreateBranchSheet.xib */, + 4A5D760F14A9A99E00DF6C68 /* PBCreateTagSheet.xib */, + 4A5D761114A9A99E00DF6C68 /* PBRemoteProgressSheet.xib */, + 4A5D761514A9A99E00DF6C68 /* RepositoryWindow.xib */, + ); + path = English.lproj; sourceTree = ""; }; - F5B161BB0EAB6E0C005A1DE1 /* Diff */ = { + 4A5D762114A9A9CC00DF6C68 /* Classes */ = { isa = PBXGroup; children = ( - F5E424140EA3E4E10046E362 /* PBDiffWindowController.h */, - F5E424130EA3E4E10046E362 /* PBDiffWindowController.m */, - F5E424160EA3E4EB0046E362 /* PBWebDiffController.m */, - F5E424170EA3E4EB0046E362 /* PBWebDiffController.h */, - ); - name = Diff; + 4A5D762514A9A9CC00DF6C68 /* Controllers */, + 4A5D764D14A9A9CC00DF6C68 /* git */, + 4A5D768814A9A9CC00DF6C68 /* GitXRelativeDateFormatter.h */, + 4A5D768914A9A9CC00DF6C68 /* GitXRelativeDateFormatter.m */, + 4A5D768A14A9A9CC00DF6C68 /* GitXScriptingConstants.h */, + 4A5D768B14A9A9CC00DF6C68 /* main.m */, + 4A2125A217C0C78A00B5B582 /* NSColor+RGB.h */, + 4A2125A317C0C78A00B5B582 /* NSColor+RGB.m */, + 373F9A1E1EAF97A200A77B4C /* NSSplitView+GitX.h */, + 373F9A1F1EAF97A200A77B4C /* NSSplitView+GitX.m */, + 4A5D768C14A9A9CC00DF6C68 /* PBChangedFile.h */, + 4A5D768D14A9A9CC00DF6C68 /* PBChangedFile.m */, + 4A5D769014A9A9CC00DF6C68 /* PBCommitList.h */, + 4A5D769114A9A9CC00DF6C68 /* PBCommitList.m */, + 4A5D769214A9A9CC00DF6C68 /* PBGraphCellInfo.h */, + 4A5D769314A9A9CC00DF6C68 /* PBGraphCellInfo.m */, + 4A5D769414A9A9CC00DF6C68 /* PBNSURLPathUserDefaultsTransfomer.h */, + 4A5D769514A9A9CC00DF6C68 /* PBNSURLPathUserDefaultsTransfomer.m */, + 4A5D769614A9A9CC00DF6C68 /* PBRefContextDelegate.h */, + 4A5D769E14A9A9CC00DF6C68 /* PBUnsortableTableHeader.h */, + 4A5D769F14A9A9CC00DF6C68 /* PBUnsortableTableHeader.m */, + 4A5D76A314A9A9CC00DF6C68 /* Terminal.h */, + 4A5D76A414A9A9CC00DF6C68 /* Util */, + 4A5D76B314A9A9CC00DF6C68 /* Views */, + 4D6E4F781E56851A004C3A6F /* PBMacros.m */, + 4D6E4F791E56851A004C3A6F /* PBMacros.h */, + ); + path = Classes; sourceTree = ""; }; - F5C6F6750E65FE2B00478D97 /* Graphing */ = { + 4A5D762514A9A9CC00DF6C68 /* Controllers */ = { isa = PBXGroup; children = ( - F5FF4E780E082E440006317A /* PBGitGrapher.h */, - F5FF4E790E082E440006317A /* PBGitGrapher.mm */, - F50FE0E10E07BE9600854FCD /* PBGitRevisionCell.h */, - F50FE0E20E07BE9600854FCD /* PBGitRevisionCell.m */, - F56CC7270E65E0AD004307B4 /* PBGitGraphLine.h */, - F56CC7300E65E0E5004307B4 /* PBGraphCellInfo.h */, - F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */, - F5C6F68B0E65FF9300478D97 /* PBGitLane.h */, - F5C6F68C0E65FF9300478D97 /* PBGitLane.mm */, - ); - name = Graphing; + 4A5D762614A9A9CC00DF6C68 /* ApplicationController.h */, + 4A5D762714A9A9CC00DF6C68 /* ApplicationController.m */, + 4A5D762A14A9A9CC00DF6C68 /* OpenRecentController.h */, + 4A5D762B14A9A9CC00DF6C68 /* OpenRecentController.m */, + 4A5D762C14A9A9CC00DF6C68 /* PBDiffWindowController.h */, + 4A5D762D14A9A9CC00DF6C68 /* PBDiffWindowController.m */, + 4A5D762E14A9A9CC00DF6C68 /* PBGitCommitController.h */, + 4A5D762F14A9A9CC00DF6C68 /* PBGitCommitController.m */, + 4A5D763014A9A9CC00DF6C68 /* PBGitHistoryController.h */, + 4A5D763114A9A9CC00DF6C68 /* PBGitHistoryController.m */, + 4A5D763414A9A9CC00DF6C68 /* PBGitSidebarController.h */, + 4A5D763514A9A9CC00DF6C68 /* PBGitSidebarController.m */, + 4A5D763614A9A9CC00DF6C68 /* PBGitWindowController.h */, + 4A5D763714A9A9CC00DF6C68 /* PBGitWindowController.m */, + 4A5D763814A9A9CC00DF6C68 /* PBHistorySearchController.h */, + 4A5D763914A9A9CC00DF6C68 /* PBHistorySearchController.m */, + 6C5244021E00C33F0051DE20 /* PBHistorySearchMode.h */, + 6C5244121E00C66E0051DE20 /* PBHistorySearchMode.m */, + 4A5D763C14A9A9CC00DF6C68 /* PBRefController.h */, + 4A5D763D14A9A9CC00DF6C68 /* PBRefController.m */, + 4A5D763E14A9A9CC00DF6C68 /* PBRepositoryDocumentController.h */, + 4A5D763F14A9A9CC00DF6C68 /* PBRepositoryDocumentController.m */, + 4A5D764014A9A9CC00DF6C68 /* PBServicesController.h */, + 4A5D764114A9A9CC00DF6C68 /* PBServicesController.m */, + 4A5D764214A9A9CC00DF6C68 /* PBViewController.h */, + 4A5D764314A9A9CC00DF6C68 /* PBViewController.m */, + 4A5D764414A9A9CC00DF6C68 /* PBWebChangesController.h */, + 4A5D764514A9A9CC00DF6C68 /* PBWebChangesController.m */, + 4A5D764614A9A9CC00DF6C68 /* PBWebController.h */, + 4A5D764714A9A9CC00DF6C68 /* PBWebController.m */, + 4A5D764814A9A9CC00DF6C68 /* PBWebDiffController.h */, + 4A5D764914A9A9CC00DF6C68 /* PBWebDiffController.m */, + 4A5D764A14A9A9CC00DF6C68 /* PBWebHistoryController.h */, + 4A5D764B14A9A9CC00DF6C68 /* PBWebHistoryController.m */, + 4D3FB3E5198AEAAF000B4A58 /* PBGitRepositoryDocument.h */, + 4D3FB3E6198AEAAF000B4A58 /* PBGitRepositoryDocument.m */, + 6C4855D81D57E4FA0027A7B4 /* PBOpenShallowRepositoryErrorRecoveryAttempter.h */, + 6C4855D91D57E4FA0027A7B4 /* PBOpenShallowRepositoryErrorRecoveryAttempter.m */, + ); + path = Controllers; sourceTree = ""; }; - F5E927E10E883D2E00056E75 /* History */ = { + 4A5D764D14A9A9CC00DF6C68 /* git */ = { isa = PBXGroup; children = ( - F5AD56770E79B78100EDAAFE /* PBCommitList.h */, - F5AD56780E79B78100EDAAFE /* PBCommitList.m */, - F5C6F6750E65FE2B00478D97 /* Graphing */, - F56524EE0E02D45200F03B52 /* PBGitCommit.h */, - F56524EF0E02D45200F03B52 /* PBGitCommit.m */, - F5C007730E731B48007B84B2 /* PBGitRef.h */, - F5C007740E731B48007B84B2 /* PBGitRef.m */, - F5FF4E160E0829C20006317A /* PBGitRevList.h */, - F5FF4E170E0829C20006317A /* PBGitRevList.mm */, - F53FF2030E7ABB5300389171 /* PBGitRevSpecifier.h */, - F53FF2040E7ABB5300389171 /* PBGitRevSpecifier.m */, - F56174550E058893001DCD79 /* PBGitTree.h */, - F56174560E058893001DCD79 /* PBGitTree.m */, - F5FC43C30EBD050800191D80 /* PBRefContextDelegate.h */, - F5FC43FC0EBD08EE00191D80 /* PBRefMenuItem.h */, - F5FC43FD0EBD08EE00191D80 /* PBRefMenuItem.m */, - ); - name = History; + 4A5D764E14A9A9CC00DF6C68 /* PBGitBinary.h */, + 4A5D764F14A9A9CC00DF6C68 /* PBGitBinary.m */, + 4A5D765014A9A9CC00DF6C68 /* PBGitCommit.h */, + 4A5D765114A9A9CC00DF6C68 /* PBGitCommit.m */, + 4A5D765414A9A9CC00DF6C68 /* PBGitDefaults.h */, + 4A5D765514A9A9CC00DF6C68 /* PBGitDefaults.m */, + 4A5D765614A9A9CC00DF6C68 /* PBGitGrapher.h */, + 4A5D765714A9A9CC00DF6C68 /* PBGitGrapher.mm */, + 4A5D765814A9A9CC00DF6C68 /* PBGitGraphLine.h */, + 4A5D765914A9A9CC00DF6C68 /* PBGitGraphLine.m */, + 4A5D765A14A9A9CC00DF6C68 /* PBGitHistoryGrapher.h */, + 4A5D765B14A9A9CC00DF6C68 /* PBGitHistoryGrapher.m */, + 4A5D765C14A9A9CC00DF6C68 /* PBGitHistoryList.h */, + 4A5D765D14A9A9CC00DF6C68 /* PBGitHistoryList.m */, + 4A5D765E14A9A9CC00DF6C68 /* PBGitIndex.h */, + 4A5D765F14A9A9CC00DF6C68 /* PBGitIndex.m */, + 4A5D766014A9A9CC00DF6C68 /* PBGitLane.h */, + 4A5D766114A9A9CC00DF6C68 /* PBGitLane.mm */, + 4A5D766214A9A9CC00DF6C68 /* PBGitRef.h */, + 4A5D766314A9A9CC00DF6C68 /* PBGitRef.m */, + 4A5D766414A9A9CC00DF6C68 /* PBGitRefish.h */, + 4A5D766514A9A9CC00DF6C68 /* PBGitRepository.h */, + 4A5D766614A9A9CC00DF6C68 /* PBGitRepository.m */, + 4A5D766714A9A9CC00DF6C68 /* PBGitRepositoryWatcher.h */, + 4A5D766814A9A9CC00DF6C68 /* PBGitRepositoryWatcher.m */, + 4A5D766B14A9A9CC00DF6C68 /* PBGitRevList.h */, + 4A5D766C14A9A9CC00DF6C68 /* PBGitRevList.mm */, + 4A5D766D14A9A9CC00DF6C68 /* PBGitRevSpecifier.h */, + 4A5D766E14A9A9CC00DF6C68 /* PBGitRevSpecifier.m */, + 4A5D767114A9A9CC00DF6C68 /* PBGitSVBranchItem.h */, + 4A5D767214A9A9CC00DF6C68 /* PBGitSVBranchItem.m */, + 4A5D767314A9A9CC00DF6C68 /* PBGitSVFolderItem.h */, + 4A5D767414A9A9CC00DF6C68 /* PBGitSVFolderItem.m */, + 4A5D767514A9A9CC00DF6C68 /* PBGitSVOtherRevItem.h */, + 4A5D767614A9A9CC00DF6C68 /* PBGitSVOtherRevItem.m */, + 4A5D767714A9A9CC00DF6C68 /* PBGitSVRemoteBranchItem.h */, + 4A5D767814A9A9CC00DF6C68 /* PBGitSVRemoteBranchItem.m */, + 4A5D767914A9A9CC00DF6C68 /* PBGitSVRemoteItem.h */, + 4A5D767A14A9A9CC00DF6C68 /* PBGitSVRemoteItem.m */, + 4A5D767B14A9A9CC00DF6C68 /* PBGitSVStageItem.h */, + 4A5D767C14A9A9CC00DF6C68 /* PBGitSVStageItem.m */, + 4A5D767D14A9A9CC00DF6C68 /* PBGitSVTagItem.h */, + 4A5D767E14A9A9CC00DF6C68 /* PBGitSVTagItem.m */, + A2F8D0E917AAB95E00580B84 /* PBGitSVStashItem.h */, + A2F8D0EA17AAB95E00580B84 /* PBGitSVStashItem.m */, + 4A5D767F14A9A9CC00DF6C68 /* PBGitTree.h */, + 4A5D768014A9A9CC00DF6C68 /* PBGitTree.m */, + 4A5D768314A9A9CC00DF6C68 /* PBGitXProtocol.h */, + 4A5D768414A9A9CC00DF6C68 /* PBGitXProtocol.m */, + A2F8D0DD17AAB32500580B84 /* PBGitStash.h */, + A2F8D0DE17AAB32500580B84 /* PBGitStash.m */, + 643952751603EF9B00BB7AFF /* PBGitSVSubmoduleItem.h */, + 643952761603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m */, + 4AB057E11652652000DE751D /* PBRepositoryFinder.h */, + 4AB057E21652652000DE751D /* PBRepositoryFinder.m */, + 2682AAB91929140E00271A4D /* GTOID+JavaScript.h */, + 2682AABA1929140E00271A4D /* GTOID+JavaScript.m */, + 4D843B3E1E5E3939004C3A6F /* PBGitRepository_PBGitBinarySupport.h */, + 4D843B3F1E5E3939004C3A6F /* PBGitRepository_PBGitBinarySupport.m */, + ); + path = git; sourceTree = ""; }; - F5E927E30E883D6800056E75 /* Commit */ = { + 4A5D76A414A9A9CC00DF6C68 /* Util */ = { isa = PBXGroup; children = ( - 93F7857D0EA3ABF100C1F443 /* PBCommitMessageView.h */, - 93F7857E0EA3ABF100C1F443 /* PBCommitMessageView.m */, - F593DF760E9E636C003A8559 /* PBFileChangesTableView.h */, - F593DF770E9E636C003A8559 /* PBFileChangesTableView.m */, - ); - name = Commit; + 4A5D76A514A9A9CC00DF6C68 /* NSApplication+GitXScripting.h */, + 4A5D76A614A9A9CC00DF6C68 /* NSApplication+GitXScripting.m */, + 4A5D76A714A9A9CC00DF6C68 /* NSFileHandleExt.h */, + 4A5D76A814A9A9CC00DF6C68 /* NSFileHandleExt.m */, + 4A5D76A914A9A9CC00DF6C68 /* NSOutlineViewExt.h */, + 4A5D76AA14A9A9CC00DF6C68 /* NSOutlineViewExt.m */, + 4A5D76AD14A9A9CC00DF6C68 /* NSString_Truncate.h */, + 4A5D76AE14A9A9CC00DF6C68 /* NSString_Truncate.m */, + 4A5D76AF14A9A9CC00DF6C68 /* PBEasyFS.h */, + 4A5D76B014A9A9CC00DF6C68 /* PBEasyFS.m */, + 4A5D76B114A9A9CC00DF6C68 /* PBEasyPipe.h */, + 4A5D76B214A9A9CC00DF6C68 /* PBEasyPipe.m */, + 4A5D768114A9A9CC00DF6C68 /* PBError.h */, + 4A5D768214A9A9CC00DF6C68 /* PBError.m */, + 6C4855C61D57DCFC0027A7B4 /* PBTerminalUtil.h */, + 6C4855C71D57DCFC0027A7B4 /* PBTerminalUtil.m */, + 4AB71FF614B7EDD400F1DFFC /* RJModalRepoSheet.h */, + 4AB71FF714B7EDD400F1DFFC /* RJModalRepoSheet.m */, + 6C2581091C2720E60080A89A /* GitXCommitCopier.h */, + 6C25810A1C2720E60080A89A /* GitXCommitCopier.m */, + 4DECFBB01E662962004C3A6F /* ObjectiveGit+PBCategories.h */, + 4DECFBB11E662962004C3A6F /* ObjectiveGit+PBCategories.m */, + 4D843B1D1E5D27C3004C3A6F /* PBTask.h */, + 4D843B1E1E5D27C3004C3A6F /* PBTask.m */, + ); + path = Util; sourceTree = ""; }; - F5E927F90E883EF600056E75 /* Commit */ = { + 4A5D76B314A9A9CC00DF6C68 /* Views */ = { isa = PBXGroup; children = ( - F59116E70E843BCB0072CCB1 /* PBGitCommitController.h */, - F59116E80E843BCB0072CCB1 /* PBGitCommitController.m */, - F5E927FA0E883F0700056E75 /* PBWebChangesController.h */, - F5E927FB0E883F0700056E75 /* PBWebChangesController.m */, - F523CEB40ED3399100DDD714 /* PBGitIndexController.h */, - F523CEB50ED3399200DDD714 /* PBGitIndexController.m */, - ); - name = Commit; - sourceTree = ""; - }; - F5EF8C880E9D498F0050906B /* History */ = { - isa = PBXGroup; - children = ( - F56526290E03D89B00F03B52 /* PBWebHistoryController.h */, - F565262A0E03D89B00F03B52 /* PBWebHistoryController.m */, - F52BCE050E84211300AA3741 /* PBGitHistoryController.h */, - F52BCE060E84211300AA3741 /* PBGitHistoryController.m */, - F574A2830EAE2EAC003F2CB1 /* PBRefController.h */, - F574A2840EAE2EAC003F2CB1 /* PBRefController.m */, - ); - name = History; + 4A5D76B414A9A9CC00DF6C68 /* GitXTextFieldCell.h */, + 4A5D76B514A9A9CC00DF6C68 /* GitXTextFieldCell.m */, + BF42F1431E025411004769FF /* GitXTextView.h */, + BF42F1321E025403004769FF /* GitXTextView.m */, + 4A5D76B614A9A9CC00DF6C68 /* GLFileView.h */, + 4A5D76B714A9A9CC00DF6C68 /* GLFileView.m */, + 4A5D76B814A9A9CC00DF6C68 /* PBAddRemoteSheet.h */, + 4A5D76B914A9A9CC00DF6C68 /* PBAddRemoteSheet.m */, + 4A5D76BA14A9A9CC00DF6C68 /* PBCloneRepositoryPanel.h */, + 4A5D76BB14A9A9CC00DF6C68 /* PBCloneRepositoryPanel.m */, + 4A5D76C014A9A9CC00DF6C68 /* PBCommitHookFailedSheet.h */, + 4A5D76C114A9A9CC00DF6C68 /* PBCommitHookFailedSheet.m */, + 4A5D76C214A9A9CC00DF6C68 /* PBCommitMessageView.h */, + 4A5D76C314A9A9CC00DF6C68 /* PBCommitMessageView.m */, + 4A5D76C414A9A9CC00DF6C68 /* PBCreateBranchSheet.h */, + 4A5D76C514A9A9CC00DF6C68 /* PBCreateBranchSheet.m */, + 4A5D76C614A9A9CC00DF6C68 /* PBCreateTagSheet.h */, + 4A5D76C714A9A9CC00DF6C68 /* PBCreateTagSheet.m */, + 4A5D76C814A9A9CC00DF6C68 /* PBFileChangesTableView.h */, + 4A5D76C914A9A9CC00DF6C68 /* PBFileChangesTableView.m */, + 4A5D76CA14A9A9CC00DF6C68 /* PBGitGradientBarView.h */, + 4A5D76CB14A9A9CC00DF6C68 /* PBGitGradientBarView.m */, + 4A5D76CC14A9A9CC00DF6C68 /* PBGitRevisionCell.h */, + 4A5D76CD14A9A9CC00DF6C68 /* PBGitRevisionCell.m */, + 4A5D76CE14A9A9CC00DF6C68 /* PBGitXMessageSheet.h */, + 4A5D76CF14A9A9CC00DF6C68 /* PBGitXMessageSheet.m */, + 4A5D76D014A9A9CC00DF6C68 /* PBIconAndTextCell.h */, + 4A5D76D114A9A9CC00DF6C68 /* PBIconAndTextCell.m */, + 4A5D76D414A9A9CC00DF6C68 /* PBQLOutlineView.h */, + 4A5D76D514A9A9CC00DF6C68 /* PBQLOutlineView.m */, + 4A5D76D614A9A9CC00DF6C68 /* PBQLTextView.h */, + 4A5D76D714A9A9CC00DF6C68 /* PBQLTextView.m */, + 4A5D76D814A9A9CC00DF6C68 /* PBRefMenuItem.h */, + 4A5D76D914A9A9CC00DF6C68 /* PBRefMenuItem.m */, + 4A5D76DA14A9A9CC00DF6C68 /* PBRemoteProgressSheet.h */, + 4A5D76DB14A9A9CC00DF6C68 /* PBRemoteProgressSheet.m */, + 4A5D776C14A9AEB000DF6C68 /* PBSourceViewAction.h */, + 4A5D776D14A9AEB000DF6C68 /* PBSourceViewAction.m */, + 4A5D776E14A9AEB000DF6C68 /* PBSourceViewBadge.h */, + 4A5D776F14A9AEB000DF6C68 /* PBSourceViewBadge.m */, + 4A5D76DC14A9A9CC00DF6C68 /* PBSourceViewCell.h */, + 4A5D76DD14A9A9CC00DF6C68 /* PBSourceViewCell.m */, + 4A5D76DE14A9A9CC00DF6C68 /* PBSourceViewItem.h */, + 4A5D76DF14A9A9CC00DF6C68 /* PBSourceViewItem.m */, + 4A5D777014A9AEB000DF6C68 /* PBSourceViewItems.h */, + 4A5D777114A9AEB000DF6C68 /* PBSourceViewRemote.h */, + 4A5D777214A9AEB000DF6C68 /* PBSourceViewRemote.m */, + ); + path = Views; sourceTree = ""; }; /* End PBXGroup section */ -/* Begin PBXHeadersBuildPhase section */ - F567CC34106E6B910059BB9D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - F567CC7B106E6BF70059BB9D /* PBGitRef.h in Headers */, - F5D3772610753BD700AAAC80 /* PBChangedFile.h in Headers */, - F5D3772710753BD700AAAC80 /* PBGitIndex.h in Headers */, - F567CC67106E6BD00059BB9D /* PBGitRepository.h in Headers */, - F567CC68106E6BD00059BB9D /* PBGitBinary.h in Headers */, - F567CC69106E6BD00059BB9D /* PBGitConfig.h in Headers */, +/* Begin PBXNativeTarget section */ + 551BF110112F371800265053 /* gitx_askpasswd */ = { + isa = PBXNativeTarget; + buildConfigurationList = 551BF119112F373E00265053 /* Build configuration list for PBXNativeTarget "gitx_askpasswd" */; + buildPhases = ( + 551BF10E112F371800265053 /* Sources */, + 551BF10F112F371800265053 /* Frameworks */, ); - runOnlyForDeploymentPostprocessing = 0; + buildRules = ( + ); + dependencies = ( + ); + name = gitx_askpasswd; + productName = gitx_askpasswd; + productReference = 551BF111112F371800265053 /* gitx_askpasswd */; + productType = "com.apple.product-type.tool"; }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ 8D1107260486CEB800E47090 /* GitX */ = { isa = PBXNativeTarget; buildConfigurationList = 26FC0A840875C7B200E6366F /* Build configuration list for PBXNativeTarget "GitX" */; buildPhases = ( - F5792DFB0EDB570C001B0C31 /* Compile libgit2 */, 8D1107290486CEB800E47090 /* Resources */, 8D11072C0486CEB800E47090 /* Sources */, 8D11072E0486CEB800E47090 /* Frameworks */, - F580E6BD0E73329C009E2D3F /* CopyFiles */, + 630A132D215EB37D00A14FA3 /* CopyFiles */, + 630A1332215EB3C100A14FA3 /* Fix ObjectiveGit.framework install_names */, F5CF04A20EAE696C00D75C81 /* Copy HTML files */, ); buildRules = ( ); dependencies = ( - 913D5E5A0E5564F400CECEA2 /* PBXTargetDependency */, - F5643A020F792B4900A579C2 /* PBXTargetDependency */, + 4D3F253118C37D9A000922D9 /* PBXTargetDependency */, + 4A774CBD142F464D00E158CC /* PBXTargetDependency */, + 551BF175112F3F3500265053 /* PBXTargetDependency */, ); name = GitX; productInstallPath = "$(HOME)/Applications"; @@ -731,64 +960,51 @@ buildRules = ( ); dependencies = ( + 4D3F253318C37D9F000922D9 /* PBXTargetDependency */, ); name = "cli tool"; productName = "cli tool"; productReference = 913D5E490E55644600CECEA2 /* gitx */; productType = "com.apple.product-type.tool"; }; - F567CC38106E6B910059BB9D /* GitXTesting */ = { - isa = PBXNativeTarget; - buildConfigurationList = F567CC3D106E6B920059BB9D /* Build configuration list for PBXNativeTarget "GitXTesting" */; - buildPhases = ( - F567CC34106E6B910059BB9D /* Headers */, - F567CC35106E6B910059BB9D /* Resources */, - F567CC36106E6B910059BB9D /* Sources */, - F567CC37106E6B910059BB9D /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = GitXTesting; - productName = GitXTesting; - productReference = F567CC39106E6B910059BB9D /* GitXTesting.framework */; - productType = "com.apple.product-type.framework"; - }; - F5886A0F0ED5D33D0066E74C /* SpeedTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = F5886A150ED5D33E0066E74C /* Build configuration list for PBXNativeTarget "SpeedTest" */; - buildPhases = ( - F5886A0C0ED5D33D0066E74C /* Resources */, - F5886A0D0ED5D33D0066E74C /* Sources */, - F5886A0E0ED5D33D0066E74C /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SpeedTest; - productName = SpeedTest; - productReference = F5886A100ED5D33D0066E74C /* SpeedTest.app */; - productType = "com.apple.product-type.application"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; + attributes = { + LastUpgradeCheck = 1000; + TargetAttributes = { + 8D1107260486CEB800E47090 = { + DevelopmentTeam = TEQMQBRC7B; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 0; + }; + }; + }; + }; + }; buildConfigurationList = 26FC0A880875C7B200E6366F /* Build configuration list for PBXProject "GitX" */; - compatibilityVersion = "Xcode 3.1"; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + Base, + ); mainGroup = 29B97314FDCFA39411CA2CEA /* GitTest */; projectDirPath = ""; projectRoot = ""; targets = ( 8D1107260486CEB800E47090 /* GitX */, 913D5E480E55644600CECEA2 /* cli tool */, - F5886A0F0ED5D33D0066E74C /* SpeedTest */, - F56439F70F792B2100A579C2 /* Generate PList Prefix */, - F567CC38106E6B910059BB9D /* GitXTesting */, + 551BF110112F371800265053 /* gitx_askpasswd */, + 4D3F252B18C37D2E000922D9 /* Generate Scripting Header */, ); }; /* End PBXProject section */ @@ -798,73 +1014,91 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - F5E92A1B0E88550E00056E75 /* empty_file.png in Resources */, 913D5E500E55645900CECEA2 /* gitx in Resources */, - 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, - F5B721C40E05CF7E00AF29DC /* MainMenu.xib in Resources */, - 911111E20E58BD5A00BF76B4 /* RepositoryWindow.xib in Resources */, - D26DC6450E782C9000C777B2 /* gitx.icns in Resources */, - F52BCE030E84208300AA3741 /* PBGitHistoryView.xib in Resources */, - F59116E60E843BB50072CCB1 /* PBGitCommitView.xib in Resources */, - F5E92A230E88569500056E75 /* new_file.png in Resources */, - F57240BB0E9678EA00D8EE66 /* deleted_file.png in Resources */, - F5E424110EA3E4D60046E362 /* PBDiffWindow.xib in Resources */, - F50A411F0EBB874C00208746 /* mainSplitterBar.tiff in Resources */, - F50A41200EBB874C00208746 /* mainSplitterDimple.tiff in Resources */, - 056438B70ED0C40B00985397 /* DetailViewTemplate.png in Resources */, - F56ADDD90ED19F9E002AC78F /* AddBranchTemplate.png in Resources */, - F56ADDDA0ED19F9E002AC78F /* AddLabelTemplate.png in Resources */, - 3BC07F4C0ED5A5C5009A7768 /* HistoryViewTemplate.png in Resources */, - 3BC07F4D0ED5A5C5009A7768 /* CommitViewTemplate.png in Resources */, - 47DBDB6A0E94EF6500671A1E /* Preferences.xib in Resources */, - 47DBDBB10E94F6CA00671A1E /* Updates.png in Resources */, - F569AE930F2CBD7C00C2FFA7 /* Credits.html in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F567CC35106E6B910059BB9D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F5886A0C0ED5D33D0066E74C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( + BFFCB5471E3EFA9F00E0B254 /* Images.xcassets in Resources */, + 551BF176112F3F4B00265053 /* gitx_askpasswd in Resources */, + 978357C5189C238300ADC689 /* DetailViewTemplate.pdf in Resources */, + 978357C2189C1F5700ADC689 /* MergeTemplate.pdf in Resources */, + 978357A7189C05B100ADC689 /* FolderTemplate.pdf in Resources */, + 978357A6189C05B100ADC689 /* FolderClosedTemplate.pdf in Resources */, + 978357BB189C19A000ADC689 /* FetchTemplate.pdf in Resources */, + 978357AE189C074500ADC689 /* RemoteBranchTemplate.pdf in Resources */, + 97CF01F918A6C5BB00E30F2B /* deleted_file.pdf in Resources */, + 978357B0189C074500ADC689 /* StageTemplate.pdf in Resources */, + 4A5D75DE14A9A90500DF6C68 /* mainSplitterBar.tiff in Resources */, + 4A5D75DF14A9A90500DF6C68 /* mainSplitterDimple.tiff in Resources */, + 978357BA189C19A000ADC689 /* AddRemoteTemplate.pdf in Resources */, + 978357C3189C1F5700ADC689 /* RebaseTemplate.pdf in Resources */, + 4A5D75F114A9A90500DF6C68 /* rewindImage.pdf in Resources */, + 978357B8189C19A000ADC689 /* AddBranchTemplate.pdf in Resources */, + 4A5D75F714A9A90500DF6C68 /* source.css in Resources */, + 4A5D75F914A9A90500DF6C68 /* UpdateKey.pem in Resources */, + 978357C1189C1F5700ADC689 /* CherryPickTemplate.pdf in Resources */, + 978357C7189C23AF00ADC689 /* TreeViewTemplate.pdf in Resources */, + 4A5D75FB14A9A90500DF6C68 /* OpenRecentPopup.xib in Resources */, + 4A5D75FC14A9A90500DF6C68 /* PBCommitHookFailedSheet.xib in Resources */, + 978357AD189C074500ADC689 /* BranchTemplate.pdf in Resources */, + 4A5D75FD14A9A90500DF6C68 /* PBDiffWindow.xib in Resources */, + 4A5D75FE14A9A90500DF6C68 /* PBGitCommitView.xib in Resources */, + 4A5D75FF14A9A90500DF6C68 /* PBGitHistoryView.xib in Resources */, + 97CF01FB18A6C5BB00E30F2B /* new_file.pdf in Resources */, + 4A5D760014A9A90500DF6C68 /* PBGitSidebarView.xib in Resources */, + 978357BC189C19A000ADC689 /* PullTemplate.pdf in Resources */, + 4A5D760114A9A90500DF6C68 /* PBGitXMessageSheet.xib in Resources */, + 4A5D761714A9A99E00DF6C68 /* InfoPlist.strings in Resources */, + 97CF01FA18A6C5BB00E30F2B /* empty_file.pdf in Resources */, + 4A5D761814A9A99E00DF6C68 /* MainMenu.xib in Resources */, + 4A5D761914A9A99E00DF6C68 /* PBAddRemoteSheet.xib in Resources */, + 978357BD189C19A000ADC689 /* PushTemplate.pdf in Resources */, + 4A5D761A14A9A99E00DF6C68 /* PBCloneRepositoryPanel.xib in Resources */, + 4A5D761C14A9A99E00DF6C68 /* PBCreateBranchSheet.xib in Resources */, + 4A5D761D14A9A99E00DF6C68 /* PBCreateTagSheet.xib in Resources */, + 4A5D761E14A9A99E00DF6C68 /* PBRemoteProgressSheet.xib in Resources */, + 4A5D762014A9A99E00DF6C68 /* RepositoryWindow.xib in Resources */, + 4A90A73514A9D24300D0DA02 /* GitX.sdef in Resources */, + 978357B9189C19A000ADC689 /* AddLabelTemplate.pdf in Resources */, + 978357AF189C074500ADC689 /* RemoteTemplate.pdf in Resources */, + 978357B1189C074500ADC689 /* TagTemplate.pdf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - F56439F60F792B2100A579C2 /* ShellScript */ = { + 4D3F252F18C37D5A000922D9 /* Generate Scripting Header */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "$(SRCROOT)/Resources/GitX.sdef", ); + name = "Generate Scripting Header"; outputPaths = ( + "$(SRCROOT)/Resources/GitX.h", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export PATH=$PATH:$HOME/bin:$HOME/local/bin:/sw/bin:/opt/local/bin:`\"$TARGET_BUILD_DIR\"/gitx --git-path`\nVERSION=$(cd \"$PROJECT_DIR\";git describe)\nLONG_VERSION=$(echo $VERSION | sed -e \"s/\\-/\\./\" -e \"s/^v//\" -e \"s/-.*//\")\nSHORT_VERSION=$(echo $VERSION | sed -e \"s/\\-.*//\" -e \"s/^v//\")\n\necho -n \"#define LONG_VERSION $LONG_VERSION\n#define GIT_VERSION $VERSION\n#define SHORT_VERSION $SHORT_VERSION\" > \"$PROJECT_TEMP_DIR/revision\"\ntouch Info.plist"; + shellScript = "# Build the scripting bridge header GitX.h if the GitX.sdef file changes\nsdp -fh --basename GitX $SRCROOT/Resources/GitX.sdef -o $SRCROOT/Resources\n"; }; - F5792DFB0EDB570C001B0C31 /* Compile libgit2 */ = { + 630A1332215EB3C100A14FA3 /* Fix ObjectiveGit.framework install_names */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( ); - name = "Compile libgit2"; + name = "Fix ObjectiveGit.framework install_names"; + outputFileListPaths = ( + ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export PATH=$PATH:$HOME/bin:$HOME/local/bin:/sw/bin:/opt/local/bin:`\"$TARGET_BUILD_DIR\"/gitx --git-path`\ngit submodule init\ngit submodule update\ncd libgit2\nrm -f libgit2.a\nmake CFLAGS=\"-arch i386 -arch ppc\"\nranlib libgit2.a"; + shellScript = "set -e\n\ncd \"$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH\"/ObjectiveGit.framework\ninstall_name_tool -change /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib @rpath/libcrypto.1.0.0.dylib ObjectiveGit\ninstall_name_tool -change /usr/local/opt/openssl/lib/libssl.1.0.0.dylib @rpath/libssl.1.0.0.dylib ObjectiveGit\n\ncd ..\n\nchmod u+w *.dylib\n\nfoo=$(python -c \"import os; print(os.path.realpath('/usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib'))\")\n\ninstall_name_tool -id @rpath/libssl.1.0.0.dylib libssl.1.0.0.dylib\ninstall_name_tool -change \"$foo\" @rpath/libcrypto.1.0.0.dylib libssl.1.0.0.dylib\ninstall_name_tool -id @rpath/libcrypto.1.0.0.dylib libcrypto.1.0.0.dylib\n"; + showEnvVarsInLog = 0; }; F5CF04A20EAE696C00D75C81 /* Copy HTML files */ = { isa = PBXShellScriptBuildPhase; @@ -879,163 +1113,217 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "resource_path=\"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH\"\nrm -rf \"$resource_path/html\"\ncp -r html \"$resource_path\""; + shellScript = "resource_path=\"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH\"\nrm -rf \"$resource_path/html\"\ncp -r Resources/html \"$resource_path\""; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 8D11072C0486CEB800E47090 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F5EF8C8E0E9D4A5D0050906B /* PBWebController.m in Sources */, - 8D11072D0486CEB800E47090 /* main.m in Sources */, - F574A2910EAE2FF4003F2CB1 /* PBGitConfig.m in Sources */, - 77C8280E06725ACE000B614F /* ApplicationController.m in Sources */, - 770B37ED0679A11B001EADE2 /* GitTest_DataModel.xcdatamodel in Sources */, - F5945E170E02B0C200706420 /* PBGitRepository.m in Sources */, - F56524BB0E02D22D00F03B52 /* NSFileHandleExt.m in Sources */, - F56524F00E02D45200F03B52 /* PBGitCommit.m in Sources */, - F565262B0E03D89B00F03B52 /* PBWebHistoryController.m in Sources */, - F56174570E058893001DCD79 /* PBGitTree.m in Sources */, - F57CC3910E05DDF2000472E2 /* PBEasyPipe.m in Sources */, - F57CC4410E05E496000472E2 /* PBGitWindowController.m in Sources */, - F513085B0E0740F2000C8BCD /* PBQLOutlineView.m in Sources */, - F5DFFA6C0E075D8800617813 /* PBEasyFS.m in Sources */, - F50FE0E30E07BE9600854FCD /* PBGitRevisionCell.m in Sources */, - F5FF4E180E0829C20006317A /* PBGitRevList.mm in Sources */, - F5FF4E7A0E082E440006317A /* PBGitGrapher.mm in Sources */, - 911111F80E594F3F00BF76B4 /* PBRepositoryDocumentController.m in Sources */, - 913D5E5F0E556A9300CECEA2 /* PBCLIProxy.m in Sources */, - F56CC7320E65E0E5004307B4 /* PBGraphCellInfo.m in Sources */, - F5C6F68D0E65FF9300478D97 /* PBGitLane.mm in Sources */, - F5C007750E731B48007B84B2 /* PBGitRef.m in Sources */, - F5AD56790E79B78100EDAAFE /* PBCommitList.m in Sources */, - F53FF2050E7ABB5300389171 /* PBGitRevSpecifier.m in Sources */, - F52BCE070E84211300AA3741 /* PBGitHistoryController.m in Sources */, - F59116E90E843BCB0072CCB1 /* PBGitCommitController.m in Sources */, - F5E926060E8827D300056E75 /* PBViewController.m in Sources */, - F5E927F80E883E7200056E75 /* PBChangedFile.m in Sources */, - F5E927FC0E883F0700056E75 /* PBWebChangesController.m in Sources */, - 91B103CC0E898EC300C84364 /* PBIconAndTextCell.m in Sources */, - F5140DC90E8A8EB20091E9F3 /* RoundedRectangle.m in Sources */, - F56244090E9684B0002B6C44 /* PBUnsortableTableHeader.m in Sources */, - F53C4DF70E97FC630022AD59 /* PBGitBinary.m in Sources */, - F593DF780E9E636C003A8559 /* PBFileChangesTableView.m in Sources */, - 93F7857F0EA3ABF100C1F443 /* PBCommitMessageView.m in Sources */, - 93CB42C20EAB7B2200530609 /* PBGitDefaults.m in Sources */, - F5E424150EA3E4E10046E362 /* PBDiffWindowController.m in Sources */, - F5E424180EA3E4EB0046E362 /* PBWebDiffController.m in Sources */, - F5FE6C030EB13BC900F30D12 /* PBServicesController.m in Sources */, - F50A41230EBB875D00208746 /* PBNiceSplitView.m in Sources */, - F5FC41F40EBCBD4300191D80 /* PBGitXProtocol.m in Sources */, - F574A2850EAE2EAC003F2CB1 /* PBRefController.m in Sources */, - F5FC43FE0EBD08EE00191D80 /* PBRefMenuItem.m in Sources */, - F523CEB60ED3399200DDD714 /* PBGitIndexController.m in Sources */, - 47DBDB580E94EDE700671A1E /* DBPrefsWindowController.m in Sources */, - 47DBDB670E94EE8B00671A1E /* PBPrefsWindowController.m in Sources */, - 47DBDBCA0E95016F00671A1E /* PBNSURLPathUserDefaultsTransfomer.m in Sources */, - F562C8870FE1766C000EC528 /* NSString_RegEx.m in Sources */, - EB2A734A0FEE3F09006601CF /* PBCollapsibleSplitView.m in Sources */, - F59F1DD5105C4FF300115F88 /* PBGitIndex.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 913D5E460E55644600CECEA2 /* Sources */ = { + 551BF10E112F371800265053 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F53C4DF90E97FCAD0022AD59 /* PBEasyPipe.m in Sources */, - F53C4DF80E97FCA70022AD59 /* PBGitBinary.m in Sources */, - 913D5E4D0E55644E00CECEA2 /* gitx.m in Sources */, + 4A5D773B14A9A9F900DF6C68 /* gitx_askpasswd_main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - F567CC36106E6B910059BB9D /* Sources */ = { + 8D11072C0486CEB800E47090 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F567CC64106E6BC80059BB9D /* PBGitRepository.m in Sources */, - F567CC65106E6BC90059BB9D /* PBGitBinary.m in Sources */, - F5D376B9107511C500AAAC80 /* PBGitIndex.m in Sources */, - F567CC66106E6BC90059BB9D /* PBGitConfig.m in Sources */, - F5D376E0107516A700AAAC80 /* PBChangedFile.m in Sources */, - F5D376CF1075139B00AAAC80 /* PBGitRef.m in Sources */, - F5D376D01075139B00AAAC80 /* PBGitRevSpecifier.m in Sources */, - F5D376C91075134D00AAAC80 /* PBGitRevList.mm in Sources */, - F567CC8E106E6FC40059BB9D /* PBEasyPipe.m in Sources */, + 4A5D76E114A9A9CC00DF6C68 /* ApplicationController.m in Sources */, + 4A5D76E314A9A9CC00DF6C68 /* OpenRecentController.m in Sources */, + 4A5D76E414A9A9CC00DF6C68 /* PBDiffWindowController.m in Sources */, + 4A5D76E514A9A9CC00DF6C68 /* PBGitCommitController.m in Sources */, + 4A5D76E614A9A9CC00DF6C68 /* PBGitHistoryController.m in Sources */, + 4A5D76E814A9A9CC00DF6C68 /* PBGitSidebarController.m in Sources */, + 4DECFBB21E662962004C3A6F /* ObjectiveGit+PBCategories.m in Sources */, + 4A5D76E914A9A9CC00DF6C68 /* PBGitWindowController.m in Sources */, + 4A5D76EA14A9A9CC00DF6C68 /* PBHistorySearchController.m in Sources */, + 6C4855C81D57DCFC0027A7B4 /* PBTerminalUtil.m in Sources */, + 4A5D76EC14A9A9CC00DF6C68 /* PBRefController.m in Sources */, + 4A5D76ED14A9A9CC00DF6C68 /* PBRepositoryDocumentController.m in Sources */, + 4A5D76EE14A9A9CC00DF6C68 /* PBServicesController.m in Sources */, + 4A5D76EF14A9A9CC00DF6C68 /* PBViewController.m in Sources */, + 4A5D76F014A9A9CC00DF6C68 /* PBWebChangesController.m in Sources */, + 4A5D76F114A9A9CC00DF6C68 /* PBWebController.m in Sources */, + 4A5D76F214A9A9CC00DF6C68 /* PBWebDiffController.m in Sources */, + 4A5D76F314A9A9CC00DF6C68 /* PBWebHistoryController.m in Sources */, + 4A5D76F414A9A9CC00DF6C68 /* PBGitBinary.m in Sources */, + 6C25810B1C2720E60080A89A /* GitXCommitCopier.m in Sources */, + 4A5D76F514A9A9CC00DF6C68 /* PBGitCommit.m in Sources */, + 6C4855DA1D57E4FA0027A7B4 /* PBOpenShallowRepositoryErrorRecoveryAttempter.m in Sources */, + 4A5D76F714A9A9CC00DF6C68 /* PBGitDefaults.m in Sources */, + 4A5D76F814A9A9CC00DF6C68 /* PBGitGrapher.mm in Sources */, + 4A5D76F914A9A9CC00DF6C68 /* PBGitGraphLine.m in Sources */, + 4A5D76FA14A9A9CC00DF6C68 /* PBGitHistoryGrapher.m in Sources */, + 4A5D76FB14A9A9CC00DF6C68 /* PBGitHistoryList.m in Sources */, + 4A5D76FC14A9A9CC00DF6C68 /* PBGitIndex.m in Sources */, + 4A5D76FD14A9A9CC00DF6C68 /* PBGitLane.mm in Sources */, + 4A5D76FE14A9A9CC00DF6C68 /* PBGitRef.m in Sources */, + 4D843B1F1E5D27C3004C3A6F /* PBTask.m in Sources */, + 4A5D76FF14A9A9CC00DF6C68 /* PBGitRepository.m in Sources */, + 4A5D770014A9A9CC00DF6C68 /* PBGitRepositoryWatcher.m in Sources */, + 4A5D770214A9A9CC00DF6C68 /* PBGitRevList.mm in Sources */, + 4A5D770314A9A9CC00DF6C68 /* PBGitRevSpecifier.m in Sources */, + 4A5D770514A9A9CC00DF6C68 /* PBGitSVBranchItem.m in Sources */, + 4A5D770614A9A9CC00DF6C68 /* PBGitSVFolderItem.m in Sources */, + 4A5D770714A9A9CC00DF6C68 /* PBGitSVOtherRevItem.m in Sources */, + 4A5D770814A9A9CC00DF6C68 /* PBGitSVRemoteBranchItem.m in Sources */, + 4A5D770914A9A9CC00DF6C68 /* PBGitSVRemoteItem.m in Sources */, + 4A5D770A14A9A9CC00DF6C68 /* PBGitSVStageItem.m in Sources */, + 4A5D770B14A9A9CC00DF6C68 /* PBGitSVTagItem.m in Sources */, + 4A5D770C14A9A9CC00DF6C68 /* PBGitTree.m in Sources */, + 4A5D770D14A9A9CC00DF6C68 /* PBError.m in Sources */, + 4A5D770E14A9A9CC00DF6C68 /* PBGitXProtocol.m in Sources */, + 4A5D771114A9A9CC00DF6C68 /* GitXRelativeDateFormatter.m in Sources */, + 4A5D771214A9A9CC00DF6C68 /* main.m in Sources */, + 2682AABB1929140E00271A4D /* GTOID+JavaScript.m in Sources */, + 4A5D771314A9A9CC00DF6C68 /* PBChangedFile.m in Sources */, + 4D3FB3E7198AEAAF000B4A58 /* PBGitRepositoryDocument.m in Sources */, + 4A5D771514A9A9CC00DF6C68 /* PBCommitList.m in Sources */, + 4A5D771614A9A9CC00DF6C68 /* PBGraphCellInfo.m in Sources */, + 4A5D771714A9A9CC00DF6C68 /* PBNSURLPathUserDefaultsTransfomer.m in Sources */, + 4A5D771B14A9A9CC00DF6C68 /* PBUnsortableTableHeader.m in Sources */, + 4D6E4F7A1E56851A004C3A6F /* PBMacros.m in Sources */, + 4A5D771D14A9A9CC00DF6C68 /* NSApplication+GitXScripting.m in Sources */, + 4A5D771E14A9A9CC00DF6C68 /* NSFileHandleExt.m in Sources */, + 4A5D771F14A9A9CC00DF6C68 /* NSOutlineViewExt.m in Sources */, + 4A5D772114A9A9CC00DF6C68 /* NSString_Truncate.m in Sources */, + 4A5D772214A9A9CC00DF6C68 /* PBEasyFS.m in Sources */, + 4A5D772314A9A9CC00DF6C68 /* PBEasyPipe.m in Sources */, + 4A5D772414A9A9CC00DF6C68 /* GitXTextFieldCell.m in Sources */, + 4A5D772514A9A9CC00DF6C68 /* GLFileView.m in Sources */, + 4A5D772614A9A9CC00DF6C68 /* PBAddRemoteSheet.m in Sources */, + 4A5D772714A9A9CC00DF6C68 /* PBCloneRepositoryPanel.m in Sources */, + 4A5D772A14A9A9CC00DF6C68 /* PBCommitHookFailedSheet.m in Sources */, + 4A5D772B14A9A9CC00DF6C68 /* PBCommitMessageView.m in Sources */, + 4A5D772C14A9A9CC00DF6C68 /* PBCreateBranchSheet.m in Sources */, + 4A5D772D14A9A9CC00DF6C68 /* PBCreateTagSheet.m in Sources */, + 4A5D772E14A9A9CC00DF6C68 /* PBFileChangesTableView.m in Sources */, + 6C5244131E00C66E0051DE20 /* PBHistorySearchMode.m in Sources */, + 4A5D772F14A9A9CC00DF6C68 /* PBGitGradientBarView.m in Sources */, + 4A5D773014A9A9CC00DF6C68 /* PBGitRevisionCell.m in Sources */, + 4A5D773114A9A9CC00DF6C68 /* PBGitXMessageSheet.m in Sources */, + 4A5D773214A9A9CC00DF6C68 /* PBIconAndTextCell.m in Sources */, + 4D843B401E5E3939004C3A6F /* PBGitRepository_PBGitBinarySupport.m in Sources */, + 4A5D773414A9A9CC00DF6C68 /* PBQLOutlineView.m in Sources */, + 4A5D773514A9A9CC00DF6C68 /* PBQLTextView.m in Sources */, + 4A5D773614A9A9CC00DF6C68 /* PBRefMenuItem.m in Sources */, + 4A5D773714A9A9CC00DF6C68 /* PBRemoteProgressSheet.m in Sources */, + 4A5D773814A9A9CC00DF6C68 /* PBSourceViewCell.m in Sources */, + 373F9A201EAF97A200A77B4C /* NSSplitView+GitX.m in Sources */, + 4A5D773914A9A9CC00DF6C68 /* PBSourceViewItem.m in Sources */, + 4A5D777314A9AEB000DF6C68 /* PBSourceViewAction.m in Sources */, + 4A5D777414A9AEB000DF6C68 /* PBSourceViewBadge.m in Sources */, + BF42F1331E025403004769FF /* GitXTextView.m in Sources */, + 4A5D777514A9AEB000DF6C68 /* PBSourceViewRemote.m in Sources */, + 4AB71FF814B7EDD400F1DFFC /* RJModalRepoSheet.m in Sources */, + 643952771603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m in Sources */, + 4AB057E31652652000DE751D /* PBRepositoryFinder.m in Sources */, + 4A2125A417C0C78A00B5B582 /* NSColor+RGB.m in Sources */, + A2F8D0DF17AAB32500580B84 /* PBGitStash.m in Sources */, + A2F8D0EB17AAB95E00580B84 /* PBGitSVStashItem.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - F5886A0D0ED5D33D0066E74C /* Sources */ = { + 913D5E460E55644600CECEA2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F5886A260ED5D4870066E74C /* PBGitGrapher.mm in Sources */, - F5886A360ED5D56E0066E74C /* PBGitTree.m in Sources */, - F5886A330ED5D5580066E74C /* PBGitRevSpecifier.m in Sources */, - F5886A310ED5D54C0066E74C /* PBGitConfig.m in Sources */, - F5886A270ED5D4870066E74C /* PBGitRevisionCell.m in Sources */, - F5886A320ED5D5510066E74C /* PBGitCommit.m in Sources */, - F5886A340ED5D55D0066E74C /* PBGitBinary.m in Sources */, - F5886A290ED5D4870066E74C /* PBGraphCellInfo.m in Sources */, - F5886A2A0ED5D4870066E74C /* PBGitLane.mm in Sources */, - F5886A1B0ED5D37C0066E74C /* NSFileHandleExt.m in Sources */, - F5886A1C0ED5D37C0066E74C /* PBEasyPipe.m in Sources */, - F5886A1D0ED5D37C0066E74C /* PBEasyFS.m in Sources */, - F5886A180ED5D3540066E74C /* PBGitRevList.mm in Sources */, - F5886A170ED5D34F0066E74C /* PBGitRepository.m in Sources */, - F5886A190ED5D3560066E74C /* PBGitRef.m in Sources */, - F5886A160ED5D3490066E74C /* speedtest.m in Sources */, + 4A5D773A14A9A9F600DF6C68 /* gitx.m in Sources */, + 4AB057E41652652000DE751D /* PBRepositoryFinder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 913D5E5A0E5564F400CECEA2 /* PBXTargetDependency */ = { + 4A774CBD142F464D00E158CC /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 913D5E480E55644600CECEA2 /* cli tool */; - targetProxy = 913D5E590E5564F400CECEA2 /* PBXContainerItemProxy */; + targetProxy = 4A774CBC142F464D00E158CC /* 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 */; }; - F5643A020F792B4900A579C2 /* PBXTargetDependency */ = { + 551BF175112F3F3500265053 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = F56439F70F792B2100A579C2 /* Generate PList Prefix */; - targetProxy = F5643A010F792B4900A579C2 /* PBXContainerItemProxy */; + target = 551BF110112F371800265053 /* gitx_askpasswd */; + targetProxy = 551BF174112F3F3500265053 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { + 4A5D760314A9A99E00DF6C68 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( - 089C165DFE840E0CC02AAC07 /* English */, + 4A5D760414A9A99E00DF6C68 /* English */, ); name = InfoPlist.strings; sourceTree = ""; }; - 47DBDB680E94EF6500671A1E /* Preferences.xib */ = { + 4A5D760514A9A99E00DF6C68 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( - 47DBDB690E94EF6500671A1E /* English */, + 638CE7582163BF7B00A2CFA4 /* Base */, ); - name = Preferences.xib; + name = MainMenu.xib; sourceTree = ""; }; - 911111E00E58BD5A00BF76B4 /* RepositoryWindow.xib */ = { + 4A5D760714A9A99E00DF6C68 /* PBAddRemoteSheet.xib */ = { isa = PBXVariantGroup; children = ( - 911111E10E58BD5A00BF76B4 /* English */, + 638CE7592163BF7B00A2CFA4 /* Base */, ); - name = RepositoryWindow.xib; + name = PBAddRemoteSheet.xib; sourceTree = ""; }; - F5B721C20E05CF7E00AF29DC /* MainMenu.xib */ = { + 4A5D760914A9A99E00DF6C68 /* PBCloneRepositoryPanel.xib */ = { isa = PBXVariantGroup; children = ( - F5B721C30E05CF7E00AF29DC /* English */, + 638CE75A2163BF7B00A2CFA4 /* Base */, ); - name = MainMenu.xib; + name = PBCloneRepositoryPanel.xib; + sourceTree = ""; + }; + 4A5D760D14A9A99E00DF6C68 /* PBCreateBranchSheet.xib */ = { + isa = PBXVariantGroup; + children = ( + 638CE75B2163BF7C00A2CFA4 /* Base */, + ); + name = PBCreateBranchSheet.xib; + sourceTree = ""; + }; + 4A5D760F14A9A99E00DF6C68 /* PBCreateTagSheet.xib */ = { + isa = PBXVariantGroup; + children = ( + 638CE75C2163BF7C00A2CFA4 /* Base */, + ); + name = PBCreateTagSheet.xib; + sourceTree = ""; + }; + 4A5D761114A9A99E00DF6C68 /* PBRemoteProgressSheet.xib */ = { + isa = PBXVariantGroup; + children = ( + 638CE75D2163BF7C00A2CFA4 /* Base */, + ); + name = PBRemoteProgressSheet.xib; + sourceTree = ""; + }; + 4A5D761514A9A99E00DF6C68 /* RepositoryWindow.xib */ = { + isa = PBXVariantGroup; + children = ( + 638CE75E2163BF7C00A2CFA4 /* Base */, + ); + name = RepositoryWindow.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ @@ -1044,52 +1332,48 @@ 26FC0A850875C7B200E6366F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)\"", - ); + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-gitx"; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = TEQMQBRC7B; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = GitX_Prefix.pch; - HEADER_SEARCH_PATHS = libgit2/src; - INFOPLIST_FILE = Info.plist; + GCC_PREFIX_HEADER = Resources/GitX_Prefix.pch; + GCC_WARN_UNDECLARED_SELECTOR = NO; + INFOPLIST_FILE = Resources/Info.plist; INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional"; - INFOPLIST_PREFIX_HEADER = $PROJECT_TEMP_DIR/revision; - INFOPLIST_PREPROCESS = YES; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = libgit2; + INSTALL_PATH = /Applications; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.codebasesaga.macOS.GitX; PRODUCT_NAME = GitX; + PROVISIONING_PROFILE_SPECIFIER = ""; + VERSIONING_SYSTEM = "apple-generic"; WRAPPER_EXTENSION = app; - ZERO_LINK = YES; }; name = Debug; }; 26FC0A860875C7B200E6366F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - DEBUG_INFORMATION_FORMAT = dwarf; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)\"", - ); - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_MODEL_TUNING = G5; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-gitx"; + CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = TEQMQBRC7B; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = GitX_Prefix.pch; + GCC_PREFIX_HEADER = Resources/GitX_Prefix.pch; GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = ""; - HEADER_SEARCH_PATHS = libgit2/src; - INFOPLIST_FILE = Info.plist; + GCC_WARN_UNDECLARED_SELECTOR = NO; + INFOPLIST_FILE = Resources/Info.plist; INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional"; - INFOPLIST_PREFIX_HEADER = $PROJECT_TEMP_DIR/revision; - INFOPLIST_PREPROCESS = YES; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = libgit2; + INSTALL_PATH = /Applications; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.codebasesaga.macOS.GitX; PRODUCT_NAME = GitX; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRIP_INSTALLED_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; WRAPPER_EXTENSION = app; }; name = Release; @@ -1097,220 +1381,163 @@ 26FC0A890875C7B200E6366F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - GCC_ENABLE_OBJC_GC = required; - GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = DEBUG_BUILD; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 0.12; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/Mac"; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_PREPROCESS = YES; - PREBINDING = NO; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/Carthage/Checkouts/objective-git/External/libgit2/include\""; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-Werror=incompatible-pointer-types"; + SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; + VALID_ARCHS = x86_64; }; name = Debug; }; 26FC0A8A0875C7B200E6366F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ( - ppc, - i386, - ); - GCC_ENABLE_OBJC_GC = required; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CURRENT_PROJECT_VERSION = 0.12; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/Mac"; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = GITX_NO_DEPRECATE; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_PREFIX_HEADER = $PROJECT_TEMP_DIR/revision; - INFOPLIST_PREPROCESS = YES; - PREBINDING = NO; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/Carthage/Checkouts/objective-git/External/libgit2/include\""; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + OTHER_CFLAGS = "-Werror=incompatible-pointer-types"; + SDKROOT = macosx; + VALID_ARCHS = x86_64; }; name = Release; }; - 913D5E4B0E55644600CECEA2 /* Debug */ = { + 4D3F252D18C37D2E000922D9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; - INSTALL_PATH = /usr/local/bin; - LIBRARY_SEARCH_PATHS = libgit2; - OTHER_LDFLAGS = ( - "-framework", - Foundation, - "-framework", - AppKit, - ); - PREBINDING = NO; - PRODUCT_NAME = gitx; - ZERO_LINK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; - 913D5E4C0E55644600CECEA2 /* Release */ = { + 4D3F252E18C37D2E000922D9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_MODEL_TUNING = G5; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; - INSTALL_PATH = /usr/local/bin; - LIBRARY_SEARCH_PATHS = libgit2; - OTHER_LDFLAGS = ( - "-framework", - Foundation, - "-framework", - AppKit, - ); - PREBINDING = NO; - PRODUCT_NAME = gitx; - ZERO_LINK = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; - F56439F80F792B2100A579C2 /* Debug */ = { + 551BF113112F371800265053 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - PRODUCT_NAME = "Generate PList Prefix"; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = gitx_askpasswd; + SKIP_INSTALL = YES; }; name = Debug; }; - F56439F90F792B2100A579C2 /* Release */ = { + 551BF114112F371800265053 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; + INFOPLIST_PREFIX_HEADER = $PROJECT_TEMP_DIR/revision; INFOPLIST_PREPROCESS = YES; - PRODUCT_NAME = "Generate PList Prefix"; - ZERO_LINK = NO; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = gitx_askpasswd; + SKIP_INSTALL = YES; }; name = Release; }; - F567CC3B106E6B920059BB9D /* Debug */ = { + 551BF115112F371800265053 /* Install */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; - HEADER_SEARCH_PATHS = libgit2/src; - INFOPLIST_FILE = "GitXTesting-Info.plist"; - INSTALL_PATH = "$(HOME)/Library/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/libgit2\"", - ); - OTHER_CFLAGS = "-DCLI"; - OTHER_LDFLAGS = ( - "-undefined", - dynamic_lookup, - "-framework", - Foundation, - "-framework", - AppKit, - ); - PREBINDING = NO; - PRODUCT_NAME = GitXTesting; - }; - name = Debug; - }; - F567CC3C106E6B920059BB9D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - GCC_ENABLE_FIX_AND_CONTINUE = NO; - GCC_MODEL_TUNING = G5; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; - INFOPLIST_FILE = "GitXTesting-Info.plist"; - INSTALL_PATH = "$(HOME)/Library/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "\"$(SRCROOT)/libgit2\"", - ); - OTHER_LDFLAGS = ( - "-framework", - Foundation, - "-framework", - AppKit, - ); - PREBINDING = NO; - PRODUCT_NAME = GitXTesting; - ZERO_LINK = NO; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = gitx_askpasswd; + SKIP_INSTALL = YES; }; - name = Release; + name = Install; }; - F5886A130ED5D33E0066E74C /* Debug */ = { + 913D5E4B0E55644600CECEA2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - CODE_SIGN_IDENTITY = ""; - COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = /System/Library/Frameworks; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 2; + CREATE_INFOPLIST_SECTION_IN_BINARY = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = GitX_Prefix.pch; - HEADER_SEARCH_PATHS = libgit2/src; - INFOPLIST_FILE = "SpeedTest-Info.plist"; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = libgit2; - OTHER_CFLAGS = "-DCLI"; - OTHER_LDFLAGS = ( - "-L.", - "-framework", - Foundation, - "-framework", - Cocoa, - ); - PREBINDING = NO; - PRODUCT_NAME = SpeedTest; - SDKROOT = macosx10.5; + GCC_PREFIX_HEADER = Resources/GitX_Prefix.pch; + INFOPLIST_FILE = "Resources/Info-gitx.plist"; + INSTALL_PATH = /usr/local/bin; + PRODUCT_BUNDLE_IDENTIFIER = "net.phere.gitx-cli"; + PRODUCT_NAME = gitx; + SKIP_INSTALL = YES; }; name = Debug; }; - F5886A140ED5D33E0066E74C /* Release */ = { + 913D5E4C0E55644600CECEA2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; + CREATE_INFOPLIST_SECTION_IN_BINARY = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/UIKit.framework/Headers/UIKit.h"; - INFOPLIST_FILE = "SpeedTest-Info.plist"; - INSTALL_PATH = "$(HOME)/Applications"; - LIBRARY_SEARCH_PATHS = libgit2; - OTHER_LDFLAGS = ( - "-framework", - Foundation, - "-framework", - UIKit, - ); - PREBINDING = NO; - PRODUCT_NAME = SpeedTest; - SDKROOT = iphoneos2.0; - ZERO_LINK = NO; + GCC_PREFIX_HEADER = Resources/GitX_Prefix.pch; + INFOPLIST_FILE = "Resources/Info-gitx.plist"; + INSTALL_PATH = /usr/local/bin; + PRODUCT_BUNDLE_IDENTIFIER = "net.phere.gitx-cli"; + PRODUCT_NAME = gitx; + SKIP_INSTALL = YES; + STRIP_INSTALLED_PRODUCT = YES; }; name = Release; }; @@ -1335,38 +1562,30 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 913D5E570E55646100CECEA2 /* Build configuration list for PBXNativeTarget "cli tool" */ = { + 4D3F252C18C37D2E000922D9 /* Build configuration list for PBXAggregateTarget "Generate Scripting Header" */ = { isa = XCConfigurationList; buildConfigurations = ( - 913D5E4B0E55644600CECEA2 /* Debug */, - 913D5E4C0E55644600CECEA2 /* Release */, + 4D3F252D18C37D2E000922D9 /* Debug */, + 4D3F252E18C37D2E000922D9 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F56439FD0F792B3600A579C2 /* Build configuration list for PBXAggregateTarget "Generate PList Prefix" */ = { + 551BF119112F373E00265053 /* Build configuration list for PBXNativeTarget "gitx_askpasswd" */ = { isa = XCConfigurationList; buildConfigurations = ( - F56439F80F792B2100A579C2 /* Debug */, - F56439F90F792B2100A579C2 /* Release */, + 551BF113112F371800265053 /* Debug */, + 551BF114112F371800265053 /* Release */, + 551BF115112F371800265053 /* Install */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F567CC3D106E6B920059BB9D /* Build configuration list for PBXNativeTarget "GitXTesting" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F567CC3B106E6B920059BB9D /* Debug */, - F567CC3C106E6B920059BB9D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F5886A150ED5D33E0066E74C /* Build configuration list for PBXNativeTarget "SpeedTest" */ = { + 913D5E570E55646100CECEA2 /* Build configuration list for PBXNativeTarget "cli tool" */ = { isa = XCConfigurationList; buildConfigurations = ( - F5886A130ED5D33E0066E74C /* Debug */, - F5886A140ED5D33E0066E74C /* Release */, + 913D5E4B0E55644600CECEA2 /* Debug */, + 913D5E4C0E55644600CECEA2 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/GitX.xcodeproj/project.xcworkspace/.gitignore b/GitX.xcodeproj/project.xcworkspace/.gitignore new file mode 100644 index 000000000..c54511205 --- /dev/null +++ b/GitX.xcodeproj/project.xcworkspace/.gitignore @@ -0,0 +1 @@ +xcuserdata/ diff --git a/GitX.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/GitX.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..b72f21038 --- /dev/null +++ b/GitX.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/GitX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/GitX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/GitX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/GitX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/GitX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..dc8d12300 --- /dev/null +++ b/GitX.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + BuildSystemType + Latest + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/GitX.xcodeproj/xcshareddata/xcschemes/GitX.xcscheme b/GitX.xcodeproj/xcshareddata/xcschemes/GitX.xcscheme new file mode 100644 index 000000000..e5b72ca1e --- /dev/null +++ b/GitX.xcodeproj/xcshareddata/xcschemes/GitX.xcscheme @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GitXTesting-Info.plist b/GitXTesting-Info.plist deleted file mode 100644 index e7411ea24..000000000 --- a/GitXTesting-Info.plist +++ /dev/null @@ -1,20 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - com.yourcompany.${PRODUCT_NAME:identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - FMWK - CFBundleSignature - ???? - CFBundleVersion - 1.0 - - diff --git a/GitX_Prefix.pch b/GitX_Prefix.pch deleted file mode 100644 index 3416e4acc..000000000 --- a/GitX_Prefix.pch +++ /dev/null @@ -1,7 +0,0 @@ -// -// Prefix header for all source files of the 'GitTest' target in the 'GitTest' project -// - -#ifdef __OBJC__ - #import -#endif diff --git a/Images/AddBranchTemplate.png b/Images/AddBranchTemplate.png deleted file mode 100644 index bd58596ac..000000000 Binary files a/Images/AddBranchTemplate.png and /dev/null differ diff --git a/Images/AddBranchTemplate.psd b/Images/AddBranchTemplate.psd deleted file mode 100644 index 8e63634af..000000000 Binary files a/Images/AddBranchTemplate.psd and /dev/null differ diff --git a/Images/AddLabelTemplate.png b/Images/AddLabelTemplate.png deleted file mode 100644 index 58237113c..000000000 Binary files a/Images/AddLabelTemplate.png and /dev/null differ diff --git a/Images/AddLabelTemplate.psd b/Images/AddLabelTemplate.psd deleted file mode 100644 index d65db8fdd..000000000 Binary files a/Images/AddLabelTemplate.psd and /dev/null differ diff --git a/Images/CommitViewTemplate.png b/Images/CommitViewTemplate.png deleted file mode 100644 index e299d70aa..000000000 Binary files a/Images/CommitViewTemplate.png and /dev/null differ diff --git a/Images/CommitViewTemplate.psd b/Images/CommitViewTemplate.psd deleted file mode 100644 index 646d23ab4..000000000 Binary files a/Images/CommitViewTemplate.psd and /dev/null differ diff --git a/Images/DetailViewTemplate.png b/Images/DetailViewTemplate.png deleted file mode 100644 index 3fa484196..000000000 Binary files a/Images/DetailViewTemplate.png and /dev/null differ diff --git a/Images/HistoryViewTemplate.png b/Images/HistoryViewTemplate.png deleted file mode 100644 index 776f2087d..000000000 Binary files a/Images/HistoryViewTemplate.png and /dev/null differ diff --git a/Images/HistoryViewTemplate.psd b/Images/HistoryViewTemplate.psd deleted file mode 100644 index edadeffb5..000000000 Binary files a/Images/HistoryViewTemplate.psd and /dev/null differ diff --git a/Images/Preferences/Updates.png b/Images/Preferences/Updates.png deleted file mode 100644 index bbd2451fc..000000000 Binary files a/Images/Preferences/Updates.png and /dev/null differ diff --git a/Images/add_branch.psd b/Images/add_branch.psd deleted file mode 100644 index 60f352251..000000000 Binary files a/Images/add_branch.psd and /dev/null differ diff --git a/Images/deleted_file.png b/Images/deleted_file.png deleted file mode 100644 index 0f924ad40..000000000 Binary files a/Images/deleted_file.png and /dev/null differ diff --git a/Images/empty_file.png b/Images/empty_file.png deleted file mode 100644 index 78ccf874a..000000000 Binary files a/Images/empty_file.png and /dev/null differ diff --git a/Images/file_template.psd b/Images/file_template.psd deleted file mode 100644 index 50192b50f..000000000 Binary files a/Images/file_template.psd and /dev/null differ diff --git a/Images/gitx_icon.psd b/Images/gitx_icon.psd deleted file mode 100644 index 38441a4a6..000000000 Binary files a/Images/gitx_icon.psd and /dev/null differ diff --git a/Images/new_file.png b/Images/new_file.png deleted file mode 100644 index 361bdc1c4..000000000 Binary files a/Images/new_file.png and /dev/null differ diff --git a/Images/site_download_background.png b/Images/site_download_background.png deleted file mode 100644 index 5748e6c26..000000000 Binary files a/Images/site_download_background.png and /dev/null differ diff --git a/NSString_RegEx.h b/NSString_RegEx.h deleted file mode 100644 index 784e9ff8c..000000000 --- a/NSString_RegEx.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// NSString_RegEx.h -// -// Created by John R Chang on 2005-11-08. -// This code is Creative Commons Public Domain. You may use it for any purpose whatsoever. -// http://creativecommons.org/licenses/publicdomain/ -// - -#import - -/* - For regular expression help, see re_format(7) man page. -*/ - -@interface NSString (RegEx) - -/* - Common are REG_ICASE and REG_NEWLINE. For other possible option flags, - see regex(3) man page. You don't need to specify REG_EXTENDED. - - is the number of subexpressions to match. - Returns an array of strings. The first string is the matching substring, - the remaining are the matching subexpressions, up to nmatch+1 number. - - If nmatch is -1, works like grep. Returns an array containing self if matching. - - Returns nil if regular expression does not match or if an error has occurred. -*/ -- (NSArray *) substringsMatchingRegularExpression:(NSString *)pattern count:(int)nmatch - options:(int)options ranges:(NSArray **)ranges error:(NSError **)error; - -- (BOOL) grep:(NSString *)pattern options:(int)options; - -@end diff --git a/NSString_RegEx.m b/NSString_RegEx.m deleted file mode 100644 index 38d384887..000000000 --- a/NSString_RegEx.m +++ /dev/null @@ -1,97 +0,0 @@ -// -// NSString_RegEx.m -// -// Created by John R Chang on 2005-11-08. -// This code is Creative Commons Public Domain. You may use it for any purpose whatsoever. -// http://creativecommons.org/licenses/publicdomain/ -// - -#import "NSString_RegEx.h" -#include - - -@implementation NSString (RegEx) - -- (NSArray *) substringsMatchingRegularExpression:(NSString *)pattern count:(int)nmatch options:(int)options ranges:(NSArray **)ranges error:(NSError **)error -{ - options |= REG_EXTENDED; - if (error) - *error = nil; - - int errcode = 0; - regex_t preg; - regmatch_t * pmatch = NULL; - NSMutableArray * outMatches = nil; - - // Compile the regular expression - errcode = regcomp(&preg, [pattern UTF8String], options); - if (errcode != 0) - goto catch_error; // regcomp error - - // Match the regular expression against substring self - pmatch = calloc(sizeof(regmatch_t), nmatch+1); - errcode = regexec(&preg, [self UTF8String], (nmatch<0 ? 0 : nmatch+1), pmatch, 0); - - /*if (errcode == REG_NOMATCH) - { - outMatches = [NSMutableArray array]; - goto catch_exit; // no match - }*/ - if (errcode != 0) - goto catch_error; // regexec error - - if (nmatch == -1) - { - outMatches = [NSArray arrayWithObject:self]; - goto catch_exit; // simple match - } - - // Iterate through pmatch - outMatches = [NSMutableArray array]; - if (ranges) - *ranges = [NSMutableArray array]; - int i; - for (i=0; i 0) - [userInfo setObject:[NSString stringWithUTF8String:errbuf] forKey:NSLocalizedDescriptionKey]; - *error = [NSError errorWithDomain:@"regerror" code:errcode userInfo:userInfo]; - } - -catch_exit: - if (pmatch) - free(pmatch); - regfree(&preg); - return outMatches; -} - -- (BOOL) grep:(NSString *)pattern options:(int)options -{ - NSArray * substrings = [self substringsMatchingRegularExpression:pattern count:-1 options:options ranges:NULL error:NULL]; - return (substrings && [substrings count] > 0); -} - -@end diff --git a/PBCLIProxy.h b/PBCLIProxy.h deleted file mode 100644 index eb4d73560..000000000 --- a/PBCLIProxy.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// PBCLIProxy.h -// GitX -// -// Created by Ciarán Walsh on 15/08/2008. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import - - -@interface PBCLIProxy : NSObject -{ - NSConnection *connection; -} -@property (retain) NSConnection* connection; -@end - -#define ConnectionName @"GitX DO Connection" -#define PBCLIProxyErrorDomain @"PBCLIProxyErrorDomain" - -@protocol GitXCliToolProtocol -- (BOOL)openRepository:(NSURL*)repositoryPath arguments: (NSArray*) args error:(NSError**)error; -- (void)openDiffWindowWithDiff:(NSString *)diff; -@end \ No newline at end of file diff --git a/PBCLIProxy.m b/PBCLIProxy.m deleted file mode 100644 index 83507d270..000000000 --- a/PBCLIProxy.m +++ /dev/null @@ -1,74 +0,0 @@ -// -// PBCLIProxy.m -// GitX -// -// Created by Ciarán Walsh on 15/08/2008. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBCLIProxy.h" -#import "PBRepositoryDocumentController.h" -#import "PBGitRevSpecifier.h" -#import "PBGitRepository.h" -#import "PBGitWindowController.h" -#import "PBGitBinary.h" -#import "PBDiffWindowController.h" - -@implementation PBCLIProxy -@synthesize connection; - -- (id)init -{ - if (self = [super init]) { - self.connection = [NSConnection new]; - [self.connection setRootObject:self]; - - if ([self.connection registerName:ConnectionName] == NO) - NSBeep(); - - } - return self; -} - -- (BOOL)openRepository:(NSURL*)repositoryPath arguments: (NSArray*) args error:(NSError**)error; -{ - // FIXME I found that creating this redundant NSURL reference was necessary to - // work around an apparent bug with GC and Distributed Objects - // I am not familiar with GC though, so perhaps I was doing something wrong. - NSURL* url = [NSURL fileURLWithPath:[repositoryPath path]]; - NSArray* arguments = [NSArray arrayWithArray:args]; - - PBGitRepository *document = [[PBRepositoryDocumentController sharedDocumentController] documentForLocation:url]; - if (!document) { - if (error) { - NSString *suggestion = [PBGitBinary path] ? @"this isn't a git repository" : @"GitX can't find your git binary"; - - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Could not create document. Perhaps %@", suggestion] - forKey:NSLocalizedFailureReasonErrorKey]; - - *error = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:2 userInfo:userInfo]; - } - return NO; - } - - if ([arguments count] > 0 && ([[arguments objectAtIndex:0] isEqualToString:@"--commit"] || - [[arguments objectAtIndex:0] isEqualToString:@"-c"])) - [document.windowController showCommitView:self]; - else { - PBGitRevSpecifier* rev = [[PBGitRevSpecifier alloc] initWithParameters:arguments]; - rev.workingDirectory = repositoryPath; - document.currentBranch = [document addBranch: rev]; - [document.windowController showHistoryView:self]; - } - [NSApp activateIgnoringOtherApps:YES]; - - return YES; -} - -- (void)openDiffWindowWithDiff:(NSString *)diff -{ - PBDiffWindowController *diffController = [[PBDiffWindowController alloc] initWithDiff:[diff copy]]; - [diffController showWindow:nil]; - [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; -} -@end diff --git a/PBCollapsibleSplitView.h b/PBCollapsibleSplitView.h deleted file mode 100644 index 1a52c0dfa..000000000 --- a/PBCollapsibleSplitView.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// PBCollapsibleSplitView.h -// GitX -// -// This is a limited subclass of a SplitView. It adds methods to aid in -// collapsing/uncollapsing subviews using the mouse or programmatically. -// Right now it only works for vertical layouts and with two subviews. -// -// Created by Johannes Gilger on 6/21/09. -// Copyright 2009 Johannes Gilger. All rights reserved. -// - -#import -#import "PBNiceSplitView.h" - -@interface PBCollapsibleSplitView : PBNiceSplitView { - CGFloat topViewMin; - CGFloat bottomViewMin; - CGFloat splitterPosition; -} - -@property (readonly) CGFloat topViewMin; -@property (readonly) CGFloat bottomViewMin; - -- (void)setTopMin:(CGFloat)topMin andBottomMin:(CGFloat)bottomMin; -- (void)uncollapse; -- (void)keyDown:(NSEvent *)event; - -@end diff --git a/PBCollapsibleSplitView.m b/PBCollapsibleSplitView.m deleted file mode 100644 index 9665cac12..000000000 --- a/PBCollapsibleSplitView.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// PBCollapsibleSplitView.m -// GitX -// -// Created by Johannes Gilger on 6/21/09. -// Copyright 2009 Johannes Gilger. All rights reserved. -// - -#import "PBCollapsibleSplitView.h" - -@implementation PBCollapsibleSplitView -@synthesize topViewMin, bottomViewMin; - -- (void)setTopMin:(CGFloat)topMin andBottomMin:(CGFloat)bottomMin { - topViewMin = topMin; - bottomViewMin = bottomMin; -} - -- (void)uncollapse { - for (NSView *subview in [self subviews]) { - if([self isSubviewCollapsed:subview]) { - [self setPosition:[self frame].size.height / 3 ofDividerAtIndex:0]; - [self adjustSubviews]; - } - } -} - -- (void)collapseSubview:(NSInteger)index { - // Already collapsed, just uncollapse - if ([self isSubviewCollapsed:[[self subviews] objectAtIndex:index]]) { - [self setPosition:splitterPosition ofDividerAtIndex:0]; - return; - } - - // Store splitterposition if the other view isn't collapsed - if (![self isSubviewCollapsed:[[self subviews] objectAtIndex:(index + 1) % 2]]) - splitterPosition = [[[self subviews] objectAtIndex:0] frame].size.height; - - if (index == 0) // Top view - [self setPosition:0.0 ofDividerAtIndex:0]; - else // Bottom view - [self setPosition:[self frame].size.height ofDividerAtIndex:0]; -} - -- (void)keyDown:(NSEvent *)event { - if (!([event modifierFlags] & NSShiftKeyMask && [event modifierFlags] & NSCommandKeyMask)) - return [super keyDown:event]; - - if ([event keyCode] == 0x07E) { // Up-Key - [self collapseSubview:0]; - [[self window] makeFirstResponder:[[self subviews] objectAtIndex:1]]; - } else if ([event keyCode] == 0x07D) { // Down-Key - [self collapseSubview:1]; - [[self window] makeFirstResponder:[[self subviews] objectAtIndex:0]]; - } -} -@end diff --git a/PBCommitList.h b/PBCommitList.h deleted file mode 100644 index 069aeae29..000000000 --- a/PBCommitList.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// PBCommitList.h -// GitX -// -// Created by Pieter de Bie on 9/11/08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import -#import "PBGitHistoryController.h" - -@class PBWebHistoryController; - -@interface PBCommitList : NSTableView { - IBOutlet WebView* webView; - IBOutlet PBWebHistoryController *webController; - IBOutlet PBGitHistoryController *controller; - - NSPoint mouseDownPoint; -} - -@property (readonly) NSPoint mouseDownPoint; -@end diff --git a/PBCommitList.m b/PBCommitList.m deleted file mode 100644 index 9f764225c..000000000 --- a/PBCommitList.m +++ /dev/null @@ -1,82 +0,0 @@ -// -// PBCommitList.m -// GitX -// -// Created by Pieter de Bie on 9/11/08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBCommitList.h" -#import "PBGitRevisionCell.h" -#import "PBWebHistoryController.h" - -@implementation PBCommitList - -@synthesize mouseDownPoint; -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL) local -{ - return NSDragOperationCopy; -} - -- (void)keyDown:(NSEvent *)event -{ - NSString* character = [event charactersIgnoringModifiers]; - - // Pass on command-shift up/down to the responder. We want the splitview to capture this. - if ([event modifierFlags] & NSShiftKeyMask && [event modifierFlags] & NSCommandKeyMask && ([event keyCode] == 0x7E || [event keyCode] == 0x7D)) { - [self.nextResponder keyDown:event]; - return; - } - - if ([character isEqualToString:@" "]) - { - if ([event modifierFlags] & NSShiftKeyMask) - [webView scrollPageUp: self]; - else - [webView scrollPageDown: self]; - } - else if ([character rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"jkcv"]].location == 0) - [webController sendKey: character]; - else - [super keyDown: event]; -} - -- (void) copy:(id)sender -{ - [controller copyCommitInfo]; -}; - -- (void)mouseDown:(NSEvent *)theEvent -{ - mouseDownPoint = [[self window] mouseLocationOutsideOfEventStream]; - [super mouseDown:theEvent]; -} - -- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows - tableColumns:(NSArray *)tableColumns - event:(NSEvent *)dragEvent - offset:(NSPointPointer)dragImageOffset -{ - NSPoint location = [self convertPointFromBase:mouseDownPoint]; - int row = [self rowAtPoint:location]; - int column = [self columnAtPoint:location]; - PBGitRevisionCell *cell = (PBGitRevisionCell *)[self preparedCellAtColumn:column row:row]; - - int index = [cell indexAtX:location.x]; - if (index == -1) - return [super dragImageForRowsWithIndexes:dragRows tableColumns:tableColumns event:dragEvent offset:dragImageOffset]; - - NSRect rect = [cell rectAtIndex:index]; - - NSImage *newImage = [[NSImage alloc] initWithSize:NSMakeSize(rect.size.width + 3, rect.size.height + 3)]; - rect.origin = NSMakePoint(0.5, 0.5); - - [newImage lockFocus]; - [cell drawLabelAtIndex:index inRect:rect]; - [newImage unlockFocus]; - - *dragImageOffset = NSMakePoint(rect.size.width / 2 + 10, 0); - return newImage; - -} -@end diff --git a/PBCommitMessageView.m b/PBCommitMessageView.m deleted file mode 100644 index 02714c089..000000000 --- a/PBCommitMessageView.m +++ /dev/null @@ -1,40 +0,0 @@ -// -// PBCommitMessageView.m -// GitX -// -// Created by Jeff Mesnil on 13/10/08. -// Copyright 2008 Jeff Mesnil (http://jmesnil.net/). All rights reserved. -// - -#import "PBCommitMessageView.h" -#import "PBGitDefaults.h" - -@implementation PBCommitMessageView - -- (void)drawRect:(NSRect)aRect -{ - NSColor *originalColor = [self backgroundColor]; - [originalColor set]; - NSRectFill(aRect); - - // draw a vertical line after the given size (used as an indicator - // for the first line of the commit message) - float characterWidth = [@" " sizeWithAttributes:[self typingAttributes]].width; - float lineWidth = characterWidth * [PBGitDefaults commitMessageViewVerticalLineLength]; - - [[NSColor lightGrayColor] set]; - // This depends upon the fact that NSTextView always redraws complete lines. - float padding = [[self textContainer] lineFragmentPadding]; - NSRect line; - line.origin.x = padding + aRect.origin.x + lineWidth; - line.origin.y = aRect.origin.y; - line.size.width = 1; - line.size.height = aRect.size.height; - NSRectFill(line); - - [self setBackgroundColor:nil]; - [super drawRect:aRect]; - [self setBackgroundColor:originalColor]; -} - -@end diff --git a/PBDiffWindow.xib b/PBDiffWindow.xib deleted file mode 100644 index e2942e675..000000000 --- a/PBDiffWindow.xib +++ /dev/null @@ -1,302 +0,0 @@ - - - - 1050 - 9F33 - 670 - 949.34 - 352.00 - - YES - - - - YES - com.apple.WebKitIBPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - PBDiffWindowController - - - FirstResponder - - - NSApplication - - - PBWebDiffController - - - 15 - 2 - {{196, 240}, {480, 270}} - 603979776 - GitX Diff - NSWindow - - {3.40282e+38, 3.40282e+38} - - - 256 - - YES - - - 274 - - YES - - YES - Apple HTML pasteboard type - Apple PICT pasteboard type - Apple URL pasteboard type - Apple Web Archive pasteboard type - NSColor pasteboard type - NSFilenamesPboardType - NSStringPboardType - NeXT RTFD pasteboard type - NeXT Rich Text Format v1.0 pasteboard type - NeXT TIFF v4.0 pasteboard type - WebURLsWithTitlesPboardType - public.url - public.url-name - - - {480, 270} - - - - - - - - YES - - YES - WebKitDefaultFixedFontSize - WebKitDefaultFontSize - WebKitMinimumFontSize - - - YES - - - - - - - YES - YES - - - {480, 270} - - - {{0, 0}, {1680, 1028}} - {3.40282e+38, 3.40282e+38} - DiffWindow - - - - - YES - - - window - - - - 6 - - - - view - - - - 9 - - - - frameLoadDelegate - - - - 10 - - - - UIDelegate - - - - 11 - - - - diffController - - - - 12 - - - - - YES - - 0 - - YES - - - - - - -2 - - - RmlsZSdzIE93bmVyA - - - -1 - - - First Responder - - - -3 - - - Application - - - 1 - - - YES - - - - - - 2 - - - YES - - - - Diff View - - - 7 - - - - - 8 - - - - - - - YES - - YES - -1.IBPluginDependency - -2.IBPluginDependency - -3.IBPluginDependency - 1.IBEditorWindowLastContentRect - 1.IBPluginDependency - 1.IBWindowTemplateEditedContentRect - 1.NSWindowTemplate.visibleAtLaunch - 1.windowTemplate.maxSize - 2.IBPluginDependency - 7.IBPluginDependency - 8.IBPluginDependency - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilderKit - {{312, 725}, {480, 270}} - com.apple.InterfaceBuilder.CocoaPlugin - {{312, 725}, {480, 270}} - - {3.40282e+38, 3.40282e+38} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.WebKitIBPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 12 - - - - YES - - PBDiffWindowController - NSWindowController - - IBProjectSource - PBDiffWindowController.h - - - - PBWebController - NSObject - - view - WebView - - - IBProjectSource - PBWebController.h - - - - PBWebDiffController - PBWebController - - diffController - PBDiffWindowController - - - IBProjectSource - PBWebDiffController.h - - - - - 0 - GitX.xcodeproj - 3 - - diff --git a/PBDiffWindowController.h b/PBDiffWindowController.h deleted file mode 100644 index 6cb1bafcd..000000000 --- a/PBDiffWindowController.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// PBDiffWindowController.h -// GitX -// -// Created by Pieter de Bie on 13-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import - - -@interface PBDiffWindowController : NSWindowController { - NSString *diff; -} - -- initWithDiff:(NSString *)diff; -@property (readonly) NSString *diff; -@end diff --git a/PBDiffWindowController.m b/PBDiffWindowController.m deleted file mode 100644 index cd5dcc555..000000000 --- a/PBDiffWindowController.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// PBDiffWindowController.m -// GitX -// -// Created by Pieter de Bie on 13-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import "PBDiffWindowController.h" - - -@implementation PBDiffWindowController -@synthesize diff; - -- (id) initWithDiff:(NSString *)aDiff -{ - if (![super initWithWindowNibName:@"PBDiffWindow"]) - return nil; - - diff = aDiff; - return self; -} - -@end diff --git a/PBEasyPipe.h b/PBEasyPipe.h deleted file mode 100644 index 84793eeb2..000000000 --- a/PBEasyPipe.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// PBEasyPipe.h -// GitX -// -// Created by Pieter de Bie on 16-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import - - -@interface PBEasyPipe : NSObject { - -} -+ (NSTask *) taskForCommand:(NSString *)cmd withArgs:(NSArray *)args inDir:(NSString *)dir; - -+ (NSFileHandle*) handleForCommand: (NSString*) cmd withArgs: (NSArray*) args; -+ (NSFileHandle*) handleForCommand: (NSString*) cmd withArgs: (NSArray*) args inDir: (NSString*) dir; - -+ (NSString*) outputForCommand: (NSString*) cmd withArgs: (NSArray*) args; -+ (NSString*) outputForCommand: (NSString*) cmd withArgs: (NSArray*) args inDir: (NSString*) dir; -+ (NSString*) outputForCommand:(NSString *) cmd - withArgs:(NSArray *) args - inDir:(NSString *) dir - retValue:(int *) ret; -+ (NSString*) outputForCommand:(NSString *) cmd - withArgs:(NSArray *) args - inDir:(NSString *) dir - inputString:(NSString *)input - retValue:(int *) ret; -+ (NSString*) outputForCommand:(NSString *) cmd - withArgs:(NSArray *) args - inDir:(NSString *) dir - byExtendingEnvironment:(NSDictionary *)dict - inputString:(NSString *)input - retValue:(int *) ret; - - -@end diff --git a/PBGitBinary.m b/PBGitBinary.m deleted file mode 100644 index 795891d7a..000000000 --- a/PBGitBinary.m +++ /dev/null @@ -1,127 +0,0 @@ -// -// PBGitBinary.m -// GitX -// -// Created by Pieter de Bie on 04-10-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitBinary.h" -#import "PBEasyPipe.h" - -@implementation PBGitBinary - -static NSString* gitPath = nil; - -+ (NSString *)versionForPath:(NSString *)path -{ - if (!path) - return nil; - - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) - return nil; - - NSString *version = [PBEasyPipe outputForCommand:path withArgs:[NSArray arrayWithObject:@"--version"]]; - if ([version hasPrefix:@"git version "]) - return [version substringFromIndex:12]; - - return nil; -} - -+ (BOOL) acceptBinary:(NSString *)path -{ - if (!path) - return NO; - - NSString *version = [self versionForPath:path]; - if (!version) - return NO; - - int c = [version compare:@"" MIN_GIT_VERSION]; - if (c == NSOrderedSame || c == NSOrderedDescending) { - gitPath = path; - return YES; - } - - NSLog(@"Found a git binary at %@, but is only version %@", path, version); - return NO; -} - -+ (void) initialize -{ - // Check what we might have in user defaults - // NOTE: Currently this should NOT have a registered default, or the searching bits below won't work - gitPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"gitExecutable"]; - if (gitPath.length > 0) { - if ([self acceptBinary:gitPath]) - return; - [[NSAlert alertWithMessageText:@"Invalid git path" - defaultButton:@"OK" - alternateButton:nil - otherButton:nil - informativeTextWithFormat:@"You entered a custom git path in the Preferences pane, " - "but this path is not a valid git v" MIN_GIT_VERSION " or higher binary. We're going to use the default " - "search paths instead"] runModal]; - } - - // Try to find the path of the Git binary - char* path = getenv("GIT_PATH"); - if (path && [self acceptBinary:[NSString stringWithUTF8String:path]]) - return; - - // No explicit path. Try it with "which" - NSString *whichPath = [PBEasyPipe outputForCommand:@"/usr/bin/which" withArgs:[NSArray arrayWithObject:@"git"]]; - if ([self acceptBinary:whichPath]) - return; - - // Still no path. Let's try some default locations. - for (NSString* location in [PBGitBinary searchLocations]) { - if ([self acceptBinary:location]) - return; - } - - NSLog(@"Could not find a git binary higher than version " MIN_GIT_VERSION); -} - -+ (NSString *) path; -{ - return gitPath; -} - -static NSMutableArray *locations = nil; - -+ (NSArray *) searchLocations -{ - if (locations) - return locations; - - locations = [NSMutableArray arrayWithObjects:@"/opt/local/bin/git", - @"/sw/bin/git", - @"/opt/git/bin/git", - @"/usr/local/bin/git", - @"/usr/local/git/bin/git", - nil]; - - [locations addObject:[@"~/bin/git" stringByExpandingTildeInPath]]; - return locations; -} - -+ (NSString *) notFoundError -{ - NSMutableString *error = [NSMutableString stringWithString: - @"Could not find a git binary version " MIN_GIT_VERSION " or higher.\n" - "Please make sure there is a git binary in one of the following locations:\n\n"]; - for (NSString *location in [PBGitBinary searchLocations]) { - [error appendFormat:@"\t%@\n", location]; - } - return error; -} - - -+ (NSString *)version -{ - return [self versionForPath:gitPath]; -} - - -@end diff --git a/PBGitCommit.h b/PBGitCommit.h deleted file mode 100644 index 9a7832e78..000000000 --- a/PBGitCommit.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// PBGitCommit.h -// GitTest -// -// Created by Pieter de Bie on 13-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import "PBGitRepository.h" -#import "PBGitTree.h" -#include "git/oid.h" - -@interface PBGitCommit : NSObject { - git_oid sha; - git_oid *parentShas; - int nParents; - - NSString* subject; - NSString* author; - NSString* details; - NSString *_patch; - NSArray* parents; - - int timestamp; - char sign; - id lineInfo; - PBGitRepository* repository; -} - -- initWithRepository:(PBGitRepository *)repo andSha:(git_oid)sha; - -- (void)addRef:(PBGitRef *)ref; -- (void)removeRef:(id)ref; - -- (NSString *)realSha; - -@property (readonly) git_oid *sha; -@property (copy) NSString* subject; -@property (copy) NSString* author; -@property (readonly) NSArray* parents; // TODO: remove this and its uses - -@property (assign) git_oid *parentShas; -@property (assign) int nParents, timestamp; - -@property (retain) NSMutableArray* refs; -@property (readonly) NSDate *date; -@property (readonly) NSString* dateString; -@property (readonly) NSString* patch; -@property (assign) char sign; - -@property (readonly) NSString* details; -@property (readonly) PBGitTree* tree; -@property (readonly) NSArray* treeContents; -@property (retain) PBGitRepository* repository; -@property (retain) id lineInfo; -@end diff --git a/PBGitCommit.m b/PBGitCommit.m deleted file mode 100644 index 9ebe3b575..000000000 --- a/PBGitCommit.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// PBGitCommit.m -// GitTest -// -// Created by Pieter de Bie on 13-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitCommit.h" -#import "PBGitDefaults.h" - -@implementation PBGitCommit - -@synthesize repository, subject, timestamp, author, parentShas, nParents, sign, lineInfo; - -- (NSArray *) parents -{ - if (nParents == 0) - return NULL; - - int i; - NSMutableArray *p = [NSMutableArray arrayWithCapacity:nParents]; - for (i = 0; i < nParents; ++i) - { - char *s = git_oid_mkhex(parentShas + i); - [p addObject:[NSString stringWithUTF8String:s]]; - free(s); - } - return p; -} - -- (NSDate *)date -{ - return [NSDate dateWithTimeIntervalSince1970:timestamp]; -} - -- (NSString *) dateString -{ - NSDateFormatter* formatter = [[NSDateFormatter alloc] initWithDateFormat:@"%Y-%m-%d %H:%M:%S" allowNaturalLanguage:NO]; - return [formatter stringFromDate: self.date]; -} - -- (NSArray*) treeContents -{ - return self.tree.children; -} - -- (git_oid *)sha -{ - return &sha; -} - -- initWithRepository:(PBGitRepository*) repo andSha:(git_oid)newSha -{ - details = nil; - repository = repo; - sha = newSha; - return self; -} - -- (NSString *)realSha -{ - char *hex = git_oid_mkhex(&sha); - NSString *str = [NSString stringWithUTF8String:hex]; - free(hex); - return str; -} - -// FIXME: Remove this method once it's unused. -- (NSString*) details -{ - return @""; -} - -- (NSString *) patch -{ - if (_patch != nil) - return _patch; - - NSString *p = [repository outputForArguments:[NSArray arrayWithObjects:@"format-patch", @"-1", @"--stdout", [self realSha], nil]]; - // Add a GitX identifier to the patch ;) - _patch = [[p substringToIndex:[p length] -1] stringByAppendingString:@"+GitX"]; - return _patch; -} - -- (PBGitTree*) tree -{ - return [PBGitTree rootForCommit: self]; -} - -- (void)addRef:(PBGitRef *)ref -{ - if (!self.refs) - self.refs = [NSMutableArray arrayWithObject:ref]; - else - [self.refs addObject:ref]; -} - -- (void)removeRef:(id)ref -{ - if (!self.refs) - return; - - [self.refs removeObject:ref]; -} - -- (NSMutableArray *)refs -{ - return [[repository refs] objectForKey:[self realSha]]; -} - -- (void) setRefs:(NSMutableArray *)refs -{ - [[repository refs] setObject:refs forKey:[self realSha]]; -} - -- (void)finalize -{ - free(parentShas); - [super finalize]; -} - -+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector -{ - return NO; -} - -+ (BOOL)isKeyExcludedFromWebScript:(const char *)name { - return NO; -} -@end diff --git a/PBGitCommitController.h b/PBGitCommitController.h deleted file mode 100644 index f29f8844d..000000000 --- a/PBGitCommitController.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// PBGitCommitController.h -// GitX -// -// Created by Pieter de Bie on 19-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import "PBViewController.h" - -@class PBGitIndexController, PBIconAndTextCell, PBWebChangesController, PBGitIndex; - -@interface PBGitCommitController : PBViewController { - // This might have to transfer over to the PBGitRepository - // object sometime - PBGitIndex *index; - - IBOutlet NSTextView *commitMessageView; - IBOutlet NSArrayController *unstagedFilesController; - IBOutlet NSArrayController *cachedFilesController; - - IBOutlet PBGitIndexController *indexController; - IBOutlet PBWebChangesController *webController; - - NSString *status; - BOOL busy; -} - -@property(copy) NSString *status; -@property(readonly) PBGitIndex *index; -@property(assign) BOOL busy; - -- (IBAction) refresh:(id) sender; -- (IBAction) commit:(id) sender; -- (IBAction)signOff:(id)sender; -@end diff --git a/PBGitCommitController.m b/PBGitCommitController.m deleted file mode 100644 index 6a773481a..000000000 --- a/PBGitCommitController.m +++ /dev/null @@ -1,187 +0,0 @@ -// -// PBGitCommitController.m -// GitX -// -// Created by Pieter de Bie on 19-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitCommitController.h" -#import "NSFileHandleExt.h" -#import "PBChangedFile.h" -#import "PBWebChangesController.h" -#import "PBGitIndex.h" - -@interface PBGitCommitController () -- (void)refreshFinished:(NSNotification *)notification; -- (void)commitStatusUpdated:(NSNotification *)notification; -- (void)commitFinished:(NSNotification *)notification; -- (void)commitFailed:(NSNotification *)notification; -- (void)amendCommit:(NSNotification *)notification; -- (void)indexChanged:(NSNotification *)notification; -- (void)indexOperationFailed:(NSNotification *)notification; -@end - -@implementation PBGitCommitController - -@synthesize status, index, busy; - -- (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller -{ - if (!(self = [super initWithRepository:theRepository superController:controller])) - return nil; - - index = [[PBGitIndex alloc] initWithRepository:theRepository workingDirectory:[NSURL fileURLWithPath:[theRepository workingDirectory]]]; - [index refresh]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshFinished:) name:PBGitIndexFinishedIndexRefresh object:index]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commitStatusUpdated:) name:PBGitIndexCommitStatus object:index]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commitFinished:) name:PBGitIndexFinishedCommit object:index]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(commitFailed:) name:PBGitIndexCommitFailed object:index]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(amendCommit:) name:PBGitIndexAmendMessageAvailable object:index]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(indexChanged:) name:PBGitIndexIndexUpdated object:index]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(indexOperationFailed:) name:PBGitIndexOperationFailed object:index]; - - return self; -} - -- (void)awakeFromNib -{ - [super awakeFromNib]; - - [commitMessageView setTypingAttributes:[NSDictionary dictionaryWithObject:[NSFont fontWithName:@"Monaco" size:12.0] forKey:NSFontAttributeName]]; - - [unstagedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasUnstagedChanges == 1"]]; - [cachedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasStagedChanges == 1"]]; - - [unstagedFilesController setSortDescriptors:[NSArray arrayWithObjects: - [[NSSortDescriptor alloc] initWithKey:@"status" ascending:false], - [[NSSortDescriptor alloc] initWithKey:@"path" ascending:true], nil]]; - [cachedFilesController setSortDescriptors:[NSArray arrayWithObject: - [[NSSortDescriptor alloc] initWithKey:@"path" ascending:true]]]; - - [cachedFilesController setAutomaticallyRearrangesObjects:NO]; - [unstagedFilesController setAutomaticallyRearrangesObjects:NO]; -} - -- (void) removeView -{ - [webController closeView]; - [super finalize]; -} - -- (NSResponder *)firstResponder; -{ - return commitMessageView; -} - -- (IBAction)signOff:(id)sender -{ - if (![repository.config valueForKeyPath:@"user.name"] || ![repository.config valueForKeyPath:@"user.email"]) - return [[repository windowController] showMessageSheet:@"User's name not set" infoText:@"Signing off a commit requires setting user.name and user.email in your git config"]; - NSString *SOBline = [NSString stringWithFormat:@"Signed-off-by: %@ <%@>", - [repository.config valueForKeyPath:@"user.name"], - [repository.config valueForKeyPath:@"user.email"]]; - - if([commitMessageView.string rangeOfString:SOBline].location == NSNotFound) { - NSArray *selectedRanges = [commitMessageView selectedRanges]; - commitMessageView.string = [NSString stringWithFormat:@"%@\n\n%@", - commitMessageView.string, SOBline]; - [commitMessageView setSelectedRanges: selectedRanges]; - } -} - -- (void) refresh:(id) sender -{ - self.busy = YES; - self.status = @"Refreshing index…"; - [index refresh]; - - // Reload refs (in case HEAD changed) - [repository reloadRefs]; -} - -- (void) updateView -{ - [self refresh:nil]; -} - -- (IBAction) commit:(id) sender -{ - if ([[NSFileManager defaultManager] fileExistsAtPath:[repository.fileURL.path stringByAppendingPathComponent:@"MERGE_HEAD"]]) { - [[repository windowController] showMessageSheet:@"Cannot commit merges" infoText:@"GitX cannot commit merges yet. Please commit your changes from the command line."]; - return; - } - - if ([[cachedFilesController arrangedObjects] count] == 0) { - [[repository windowController] showMessageSheet:@"No changes to commit" infoText:@"You must first stage some changes before committing"]; - return; - } - - NSString *commitMessage = [commitMessageView string]; - if ([commitMessage length] < 3) { - [[repository windowController] showMessageSheet:@"Commitmessage missing" infoText:@"Please enter a commit message before committing"]; - return; - } - - [cachedFilesController setSelectionIndexes:[NSIndexSet indexSet]]; - [unstagedFilesController setSelectionIndexes:[NSIndexSet indexSet]]; - - self.busy = YES; - [commitMessageView setEditable:NO]; - - [index commitWithMessage:commitMessage]; -} - - -# pragma mark PBGitIndex Notification handling -- (void)refreshFinished:(NSNotification *)notification -{ - self.busy = NO; - self.status = @"Index refresh finished"; -} - -- (void)commitStatusUpdated:(NSNotification *)notification -{ - self.status = [[notification userInfo] objectForKey:@"description"]; -} - -- (void)commitFinished:(NSNotification *)notification -{ - [commitMessageView setEditable:YES]; - [commitMessageView setString:@""]; - [webController setStateMessage:[NSString stringWithFormat:[[notification userInfo] objectForKey:@"description"]]]; -} - -- (void)commitFailed:(NSNotification *)notification -{ - self.busy = NO; - NSString *reason = [[notification userInfo] objectForKey:@"description"]; - self.status = [@"Commit failed: " stringByAppendingString:reason]; - [commitMessageView setEditable:YES]; - [[repository windowController] showMessageSheet:@"Commit failed" infoText:reason]; -} - -- (void)amendCommit:(NSNotification *)notification -{ - // Replace commit message with the old one if it's less than 3 characters long. - // This is just a random number. - if ([[commitMessageView string] length] > 3) - return; - - NSString *message = [[notification userInfo] objectForKey:@"message"]; - commitMessageView.string = message; -} - -- (void)indexChanged:(NSNotification *)notification -{ - [cachedFilesController rearrangeObjects]; - [unstagedFilesController rearrangeObjects]; -} - -- (void)indexOperationFailed:(NSNotification *)notification -{ - [[repository windowController] showMessageSheet:@"Index operation failed" infoText:[[notification userInfo] objectForKey:@"description"]]; -} - -@end diff --git a/PBGitCommitView.xib b/PBGitCommitView.xib deleted file mode 100644 index 5b8021f88..000000000 --- a/PBGitCommitView.xib +++ /dev/null @@ -1,1849 +0,0 @@ - - - - 1050 - 9L31a - 677 - 949.54 - 353.00 - - YES - - - - - YES - com.apple.WebKitIBPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - PBGitCommitController - - - FirstResponder - - - NSApplication - - - - 274 - - YES - - - 292 - {{27, 7}, {305, 17}} - - YES - - 67239488 - 272630784 - Ready to commit - - LucidaGrande - 1.300000e+01 - 1044 - - - - 6 - System - controlColor - - 3 - MC42NjY2NjY2OQA - - - - 6 - System - controlTextColor - - 3 - MAA - - - - - - - 274 - - YES - - - 274 - - YES - - YES - Apple HTML pasteboard type - Apple PDF pasteboard type - Apple PICT pasteboard type - Apple URL pasteboard type - Apple Web Archive pasteboard type - NSColor pasteboard type - NSFilenamesPboardType - NSStringPboardType - NeXT RTFD pasteboard type - NeXT Rich Text Format v1.0 pasteboard type - NeXT TIFF v4.0 pasteboard type - WebURLsWithTitlesPboardType - public.png - public.url - public.url-name - - - {852, 173} - - - - - - - - YES - - YES - WebKitDefaultFixedFontSize - WebKitDefaultFontSize - WebKitMinimumFontSize - - - YES - - - - - - - YES - YES - - - - 256 - - YES - - - 36 - - YES - - - 256 - - YES - - - 4370 - - YES - - - 2304 - - YES - - - 4352 - {189, 194} - - YES - - - 256 - {{244, 0}, {16, 17}} - - - YES - - 1.860000e+02 - 1.000000e+01 - 3.402823e+38 - - 75628032 - 0 - - - LucidaGrande - 1.100000e+01 - 3100 - - - 6 - System - headerColor - - 3 - MQA - - - - 6 - System - headerTextColor - - - - - 337772096 - 133632 - Text Cell - - - - 6 - System - controlBackgroundColor - - - - - 3 - YES - - - - 3.000000e+00 - 2.000000e+00 - - - 6 - System - gridColor - - 3 - MC41AA - - - 1.500000e+01 - -566231040 - 4 - 15 - 0 - YES - - - {{1, 1}, {189, 194}} - - - - - 4 - - - - -2147483392 - {{174, 1}, {15, 178}} - - - _doScroller: - 9.933775e-01 - - - - -2147483392 - {{1, 179}, {173, 15}} - - 1 - - _doScroller: - 9.947090e-01 - - - {{-1, -1}, {191, 196}} - - - 562 - - - - QSAAAEEgAABBiAAAQYgAAA - - - {190, 200} - - - - {190, 215} - - {0, 0} - - 67239424 - 0 - Unstaged Changes - - - 6 - System - textBackgroundColor - - - - 3 - MCAwLjgwMDAwMDAxAA - - - - 0 - 0 - 2 - NO - - - - 36 - - YES - - - 256 - - YES - - - 289 - {{339, 0}, {96, 32}} - - YES - - 67239424 - 134217728 - Commit - - - -2038284033 - 301990017 - - DQ - 200 - 25 - - - - - 274 - - YES - - - 2304 - - YES - - - 2322 - - YES - - YES - Apple HTML pasteboard type - Apple PDF pasteboard type - Apple PICT pasteboard type - Apple PNG pasteboard type - Apple URL pasteboard type - CorePasteboardFlavorType 0x6D6F6F76 - CorePasteboardFlavorType 0x75726C20 - NSColor pasteboard type - NSFilenamesPboardType - NSStringPboardType - NeXT Encapsulated PostScript v1.2 pasteboard type - NeXT RTFD pasteboard type - NeXT Rich Text Format v1.0 pasteboard type - NeXT TIFF v4.0 pasteboard type - NeXT font pasteboard type - NeXT ruler pasteboard type - WebURLsWithTitlesPboardType - - - {427, 14} - - - - - - - - - - - YES - - - 6 - - - - 4.270000e+02 - 1 - - - 11235 - - - - YES - - YES - NSBackgroundColor - NSColor - - - YES - - 6 - System - selectedTextBackgroundColor - - - - 6 - System - selectedTextColor - - - - - - - YES - - YES - NSColor - NSUnderline - - - YES - - 1 - MCAwIDEAA - - - - - - - 6 - {1161, 1e+07} - {223, 0} - - - - {{1, 1}, {427, 157}} - - - - - - {4, -5} - 1 - - 4 - - - - -2147483392 - {{346, 1}, {15, 164}} - - - _doScroller: - 9.916667e-01 - - - - 256 - {{-100, -100}, {87, 18}} - - 1 - - _doScroller: - 1.000000e+00 - 9.456522e-01 - - - {{0, 36}, {429, 159}} - - - 530 - - - - - - - 292 - {{-2, 9}, {65, 18}} - - YES - - -2080244224 - 0 - Amend - - - 1211912703 - 2 - - NSImage - NSSwitch - - - NSSwitch - - - - 200 - 25 - - - - - 289 - {{243, 0}, {96, 32}} - - YES - - 67239424 - 134217728 - Sign-Off - - - -2038284033 - 129 - - - 200 - 25 - - - - {429, 200} - - - - {{199, 0}, {429, 215}} - - {0, 0} - - 67239424 - 0 - Commit Message - - - - 3 - MCAwLjgwMDAwMDAxAA - - - - 0 - 0 - 2 - NO - - - - 17 - - YES - - - 256 - - YES - - - 4370 - - YES - - - 2304 - - YES - - - 4352 - {214, 194} - - 1 - YES - - - 256 - {{244, 0}, {16, 17}} - - - YES - - 2.110000e+02 - 1.000000e+01 - 3.402823e+38 - - 75628032 - 0 - - - - - - - 67239488 - 133632 - Text Cell - - - - - - 3 - YES - YES - - - - 3.000000e+00 - 2.000000e+00 - - - 1.500000e+01 - -566231040 - 4 - 15 - 0 - YES - - - {{1, 1}, {214, 194}} - - - - - 4 - - - - -2147483392 - {{257, 1}, {15, 246}} - - - _doScroller: - 9.951923e-01 - - - - -2147483392 - {{1, 247}, {256, 15}} - - 1 - - _doScroller: - 9.003322e-01 - - - {{0, -1}, {216, 196}} - - - 562 - - - - QSAAAEEgAABBiAAAQYgAAA - - - {215, 200} - - - - {{637, 0}, {215, 215}} - - {0, 0} - - 67239424 - 0 - Staged Changes - - - - 3 - MCAwLjgwMDAwMDAxAA - - - - 0 - 0 - 2 - NO - - - {{0, 182}, {852, 215}} - - YES - - - {{0, 35}, {852, 397}} - - CommitViewSplitView - - - - 1316 - - {{6, 7}, {16, 16}} - - 28938 - 1.600000e+01 - 1.000000e+02 - - - {852, 432} - - NSView - - - YES - - - - YES - path - icon - - YES - - YES - YES - YES - - - - YES - value - description - path - icon - commitBlobSHA - - YES - - YES - YES - YES - - - PBWebChangesController - - - - F94591D2-A188-4B08-A8B2-8C8CEC03CB14 - - - YES - YES - YES - YES - 1 - 1 - - YES - - YES - 1E431E79-1591-49E7-9E17-49497CA4622A - NSToolbarSeparatorItem - - - YES - - - 1E431E79-1591-49E7-9E17-49497CA4622A - - View - View selector - - - - 268 - {{0, 14}, {87, 25}} - - 3 - YES - - 67239424 - 0 - - - - YES - - 4.000000e+01 - - NSImage - HistoryViewTemplate - - - History View - 0 - - - 4.000000e+01 - - NSImage - CommitViewTemplate - - - Commit View - YES - 0 - - - 1 - 2 - - - - - - {87, 25} - {87, 25} - YES - YES - 3 - YES - 0 - - - NSToolbarSeparatorItem - - Separator - - - - - - {12, 5} - {12, 1000} - YES - YES - -1 - YES - 0 - - YES - YES - - - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - - - - - - YES - - - - - YES - - - - - YES - - - - PBGitIndexController - - - - - YES - - - view - - - - 44 - - - - cachedFilesController - - - - 97 - - - - unstagedFilesController - - - - 98 - - - - controller - - - - 101 - - - - value: arrangedObjects.path - - - - - - value: arrangedObjects.path - value - arrangedObjects.path - 2 - - - 122 - - - - view - - - - 136 - - - - frameLoadDelegate - - - - 137 - - - - value: arrangedObjects.path - - - - - - value: arrangedObjects.path - value - arrangedObjects.path - 2 - - - 139 - - - - cachedFilesController - - - - 155 - - - - unstagedFilesController - - - - 156 - - - - commit: - - - - 212 - - - - commitMessageView - - - - 213 - - - - value: status - - - - - - value: status - value - status - 2 - - - 216 - - - - animate: busy - - - - - - animate: busy - animate - busy - 2 - - - 222 - - - - viewToolbar - - - - 241 - - - - webController - - - - 253 - - - - indexController - - - - 256 - - - - commitController - - - - 257 - - - - delegate - - - - 258 - - - - delegate - - - - 259 - - - - unstagedFilesController - - - - 260 - - - - stagedFilesController - - - - 261 - - - - indexController - - - - 262 - - - - stagedTable - - - - 263 - - - - unstagedTable - - - - 264 - - - - rowClicked: - - - - 268 - - - - rowClicked: - - - - 269 - - - - dataSource - - - - 276 - - - - dataSource - - - - 277 - - - - signOff: - - - - 280 - - - - contentArray: index.indexChanges - - - - - - contentArray: index.indexChanges - contentArray - index.indexChanges - 2 - - - 281 - - - - contentArray: index.indexChanges - - - - - - contentArray: index.indexChanges - contentArray - index.indexChanges - 2 - - - 282 - - - - value: index.amend - - - - - - value: index.amend - value - index.amend - 2 - - - 283 - - - - - YES - - 0 - - YES - - - - - - -2 - - - RmlsZSdzIE93bmVyA - - - -1 - - - First Responder - - - -3 - - - Application - - - 1 - - - YES - - - - - - - - 77 - - - - - 81 - - - Unstaged Files - - - 86 - - - Cached Files - - - 96 - - - Diff Controller - - - 2 - - - YES - - - - Status label - - - 42 - - - - - 186 - - - YES - - - - - - - 125 - - - - - 209 - - - YES - - - - - - - - 208 - - - YES - - - - - - 207 - - - YES - - - - - - - - - 206 - - - YES - - - - - - 45 - - - YES - - - - - - - - 48 - - - YES - - - - - - 47 - - - - - 46 - - - - - 104 - - - YES - - - - - - 105 - - - - - 163 - - - YES - - - - - - 130 - - - YES - - - - - - - - 133 - - - - - 132 - - - - - 131 - - - - - 164 - - - - - 54 - - - YES - - - - - - - - 57 - - - YES - - - - - - 56 - - - - - 55 - - - - - 113 - - - YES - - - - - - 114 - - - - - 217 - - - - - 225 - - - YES - - - - - Commit Toolbar - - - 226 - - - YES - - - - - - 227 - - - - - 239 - - - YES - - - - - - 240 - - - - - 247 - - - YES - - - - - - 248 - - - - - 254 - - - - - 278 - - - YES - - - - - - 279 - - - - - - - YES - - YES - -1.IBPluginDependency - -2.IBPluginDependency - -3.IBPluginDependency - 1.IBEditorWindowLastContentRect - 1.IBPluginDependency - 1.IBViewEditorWindowController.showingBoundsRectangles - 1.IBViewEditorWindowController.showingLayoutRectangles - 105.CustomClassName - 114.CustomClassName - 125.IBPluginDependency - 130.IBPluginDependency - 131.IBPluginDependency - 132.IBPluginDependency - 133.CustomClassName - 133.IBPluginDependency - 163.IBPluginDependency - 164.IBPluginDependency - 186.CustomClassName - 2.IBPluginDependency - 217.IBPluginDependency - 225.IBEditorWindowLastContentRect - 225.IBPluginDependency - 225.editorWindowContentRectSynchronizationRect - 227.IBPluginDependency - 239.IBPluginDependency - 240.IBPluginDependency - 247.IBPluginDependency - 248.IBPluginDependency - 254.IBPluginDependency - 278.IBPluginDependency - 279.IBPluginDependency - 42.IBPluginDependency - 45.IBPluginDependency - 46.IBPluginDependency - 47.IBPluginDependency - 48.CustomClassName - 48.IBPluginDependency - 54.IBPluginDependency - 55.IBPluginDependency - 56.IBPluginDependency - 57.CustomClassName - 57.IBPluginDependency - 81.IBPluginDependency - 86.IBPluginDependency - 96.IBPluginDependency - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilderKit - {{428, 510}, {852, 432}} - com.apple.InterfaceBuilder.CocoaPlugin - - - PBIconAndTextCell - PBIconAndTextCell - com.apple.WebKitIBPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBCommitMessageView - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBNiceSplitView - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{295, 587}, {616, 169}} - com.apple.InterfaceBuilder.CocoaPlugin - {{132, 614}, {616, 0}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBFileChangesTableView - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBFileChangesTableView - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 283 - - - - YES - - PBCommitMessageView - NSTextView - - IBProjectSource - PBCommitMessageView.h - - - - PBFileChangesTableView - NSTableView - - IBProjectSource - PBFileChangesTableView.h - - - - PBGitCommitController - PBViewController - - YES - - YES - commit: - refresh: - signOff: - - - YES - id - id - id - - - - YES - - YES - cachedFilesController - commitMessageView - indexController - unstagedFilesController - webController - - - YES - NSArrayController - NSTextView - PBGitIndexController - NSArrayController - PBWebChangesController - - - - IBProjectSource - PBGitCommitController.h - - - - PBGitIndexController - NSObject - - YES - - YES - rowClicked: - tableClicked: - - - YES - NSCell - NSTableView - - - - YES - - YES - commitController - stagedFilesController - stagedTable - unstagedFilesController - unstagedTable - - - YES - PBGitCommitController - NSArrayController - NSTableView - NSArrayController - NSTableView - - - - IBProjectSource - PBGitIndexController.h - - - - PBIconAndTextCell - NSTextFieldCell - - IBProjectSource - PBIconAndTextCell.h - - - - PBNiceSplitView - NSSplitView - - IBProjectSource - PBNiceSplitView.h - - - - PBViewController - NSViewController - - viewToolbar - NSToolbar - - - IBProjectSource - PBViewController.h - - - - PBWebChangesController - PBWebController - - YES - - YES - cachedFilesController - controller - indexController - unstagedFilesController - - - YES - NSArrayController - PBGitCommitController - PBGitIndexController - NSArrayController - - - - IBProjectSource - PBWebChangesController.h - - - - PBWebController - NSObject - - YES - - YES - repository - view - - - YES - id - WebView - - - - IBProjectSource - PBWebController.h - - - - - 0 - GitX.xcodeproj - 3 - - diff --git a/PBGitConfig.h b/PBGitConfig.h deleted file mode 100644 index 9875a3c14..000000000 --- a/PBGitConfig.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// PBGitConfig.h -// GitX -// -// Created by Pieter de Bie on 14-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import -#import "PBGitBinary.h" -#import "PBEasyPipe.h" - -@interface PBGitConfig : NSObject { - NSString *repositoryPath; -} - -- init; -- initWithRepository:(NSString *)path; -@end diff --git a/PBGitConfig.m b/PBGitConfig.m deleted file mode 100644 index 9166cc4c8..000000000 --- a/PBGitConfig.m +++ /dev/null @@ -1,91 +0,0 @@ -// -// PBGitConfig.m -// GitX -// -// Created by Pieter de Bie on 14-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import "PBGitConfig.h" - - -@implementation PBGitConfig - -- init -{ - repositoryPath = nil; - return self; -} - -- initWithRepository:(NSString *)path -{ - repositoryPath = path; - return self; -} - -- (void) writeValue:(NSString *)value forKey:(NSString *)key global:(BOOL)global -{ - [self willChangeValueForKey:[key substringToIndex:[key rangeOfString:@"."].location]]; - - NSMutableArray *array = [NSMutableArray arrayWithObject:@"config"]; - if (global) - [array addObject:@"--global"]; - else { - [array addObject:@"-f"]; - [array addObject:[repositoryPath stringByAppendingPathComponent:@"config"]]; - } - - [array addObject:key]; - [array addObject:value]; - - int ret; - [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:array inDir:nil retValue:&ret]; - if (ret) - NSLog(@"Writing to config file failed!"); - [self didChangeValueForKey:[key substringToIndex:[key rangeOfString:@"."].location]]; -} - -- valueForKeyPath:(NSString *)path -{ - NSMutableArray *arguments = [NSMutableArray array]; - if (repositoryPath) - [arguments addObject:[NSString stringWithFormat:@"--git-dir=%@", repositoryPath]]; - - [arguments addObject:@"config"]; - [arguments addObject:@"--get"]; - [arguments addObject:path]; - - int ret; - NSString *value = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:nil retValue:&ret]; - - if (ret) - return nil; - - return value; -} - -- (void) setValue:(id)value forKeyPath:(NSString *)path -{ - // Check if the config option is local. In that case, - // write it local - if (repositoryPath) { - NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"config", @"-f", [repositoryPath stringByAppendingPathComponent:@"config"], @"--get", path, nil]; - int ret; - [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:nil retValue:&ret]; - - if (!ret) // it's local - return [self writeValue:value forKey:path global:NO]; - } - - // Check if it exists globally. In that case, write it as a global - - NSArray *arguments = [NSArray arrayWithObjects:@"config", @"--global", @"--get", path, nil]; - int ret; - [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:nil retValue:&ret]; - if (!ret) // It exists globally - return [self writeValue:value forKey:path global:YES]; - - // It doesn't exist at all. Write it locally. - [self writeValue:value forKey:path global:NO]; -} -@end diff --git a/PBGitDefaults.h b/PBGitDefaults.h deleted file mode 100644 index fec243007..000000000 --- a/PBGitDefaults.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// PBGitDefaults.h -// GitX -// -// Created by Jeff Mesnil on 19/10/08. -// Copyright 2008 Jeff Mesnil (http://jmesnil.net/). All rights reserved. -// - -@interface PBGitDefaults : NSObject -{ - -} - -+ (int) commitMessageViewVerticalLineLength; -+ (BOOL) isGistEnabled; -+ (BOOL) isGravatarEnabled; -+ (BOOL) confirmPublicGists; -+ (BOOL) isGistPublic; -+ (BOOL)showWhitespaceDifferences; -+ (BOOL)openCurDirOnLaunch; -+ (BOOL)showOpenPanelOnLaunch; - -@end diff --git a/PBGitDefaults.m b/PBGitDefaults.m deleted file mode 100644 index dc9cfd1af..000000000 --- a/PBGitDefaults.m +++ /dev/null @@ -1,85 +0,0 @@ -// -// PBGitDefaults.m -// GitX -// -// Created by Jeff Mesnil on 19/10/08. -// Copyright 2008 Jeff Mesnil (http://jmesnil.net/). All rights reserved. -// - -#import "PBGitDefaults.h" - -#define kDefaultVerticalLineLength 50 -#define kCommitMessageViewVerticalLineLength @"PBCommitMessageViewVerticalLineLength" -#define kEnableGist @"PBEnableGist" -#define kEnableGravatar @"PBEnableGravatar" -#define kConfirmPublicGists @"PBConfirmPublicGists" -#define kPublicGist @"PBGistPublic" -#define kShowWhitespaceDifferences @"PBShowWhitespaceDifferences" -#define kOpenCurDirOnLaunch @"PBOpenCurDirOnLaunch" -#define kShowOpenPanelOnLaunch @"PBShowOpenPanelOnLaunch" - -@implementation PBGitDefaults - -+ (void)initialize -{ - NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary]; - [defaultValues setObject:[NSNumber numberWithInt:kDefaultVerticalLineLength] - forKey:kCommitMessageViewVerticalLineLength]; - [defaultValues setObject:[NSNumber numberWithBool:YES] - forKey:kEnableGist]; - [defaultValues setObject:[NSNumber numberWithBool:YES] - forKey:kEnableGravatar]; - [defaultValues setObject:[NSNumber numberWithBool:YES] - forKey:kConfirmPublicGists]; - [defaultValues setObject:[NSNumber numberWithBool:NO] - forKey:kPublicGist]; - [defaultValues setObject:[NSNumber numberWithBool:YES] - forKey:kShowWhitespaceDifferences]; - [defaultValues setObject:[NSNumber numberWithBool:YES] - forKey:kOpenCurDirOnLaunch]; - [defaultValues setObject:[NSNumber numberWithBool:YES] - forKey:kShowOpenPanelOnLaunch]; - [[NSUserDefaults standardUserDefaults] registerDefaults:defaultValues]; -} - -+ (int) commitMessageViewVerticalLineLength -{ - return [[NSUserDefaults standardUserDefaults] integerForKey:kCommitMessageViewVerticalLineLength]; -} - -+ (BOOL) isGistEnabled -{ - return [[NSUserDefaults standardUserDefaults] boolForKey:kEnableGist]; -} - -+ (BOOL) isGravatarEnabled -{ - return [[NSUserDefaults standardUserDefaults] boolForKey:kEnableGravatar]; -} - -+ (BOOL) confirmPublicGists -{ - return [[NSUserDefaults standardUserDefaults] boolForKey:kConfirmPublicGists]; -} - -+ (BOOL) isGistPublic -{ - return [[NSUserDefaults standardUserDefaults] boolForKey:kPublicGist]; -} - -+ (BOOL)showWhitespaceDifferences -{ - return [[NSUserDefaults standardUserDefaults] boolForKey:kShowWhitespaceDifferences]; -} - -+ (BOOL)openCurDirOnLaunch -{ - return [[NSUserDefaults standardUserDefaults] boolForKey:kOpenCurDirOnLaunch]; -} - -+ (BOOL)showOpenPanelOnLaunch -{ - return [[NSUserDefaults standardUserDefaults] boolForKey:kShowOpenPanelOnLaunch]; -} - -@end diff --git a/PBGitGrapher.mm b/PBGitGrapher.mm deleted file mode 100644 index f9daf8492..000000000 --- a/PBGitGrapher.mm +++ /dev/null @@ -1,163 +0,0 @@ -// -// PBGitGrapher.m -// GitX -// -// Created by Pieter de Bie on 17-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitGrapher.h" -#import "PBGitCommit.h" -#import "PBGitLane.h" -#import "PBGitGraphLine.h" -#import -#import "git/oid.h" - -using namespace std; - -@implementation PBGitGrapher - -#define MAX_LANES 32 - -- (id) initWithRepository: (PBGitRepository*) repo -{ - pl = new std::list; - - PBGitLane::resetColors(); - return self; -} - -void add_line(struct PBGitGraphLine *lines, int *nLines, int upper, int from, int to, int index) -{ - // TODO: put in one thing - struct PBGitGraphLine a = { upper, from, to, index }; - lines[(*nLines)++] = a; -} - -- (void) decorateCommit: (PBGitCommit *) commit -{ - int i = 0, newPos = -1; - std::list *currentLanes = new std::list; - std::list *previousLanes = (std::list *)pl; - - int maxLines = (previousLanes->size() + commit.nParents + 2) * 2; - struct PBGitGraphLine *lines = (struct PBGitGraphLine *)malloc(sizeof(struct PBGitGraphLine) * maxLines); - int currentLine = 0; - - PBGitLane *currentLane = NULL; - BOOL didFirst = NO; - - // First, iterate over earlier columns and pass through any that don't want this commit - if (previous != nil) { - // We can't count until numColumns here, as it's only used for the width of the cell. - std::list::iterator it = previousLanes->begin(); - for (; it != previousLanes->end(); ++it) { - i++; - // This is our commit! We should do a "merge": move the line from - // our upperMapping to their lowerMapping - if ((*it)->isCommit([commit sha])) { - if (!didFirst) { - didFirst = YES; - currentLanes->push_back(*it); - currentLane = currentLanes->back(); - newPos = currentLanes->size(); - add_line(lines, ¤tLine, 1, i, newPos,(*it)->index()); - if (commit.nParents) - add_line(lines, ¤tLine, 0, newPos, newPos,(*it)->index()); - } - else { - add_line(lines, ¤tLine, 1, i, newPos,(*it)->index()); - delete *it; - } - } - else { - // We are not this commit. - currentLanes->push_back(*it); - add_line(lines, ¤tLine, 1, i, currentLanes->size(),(*it)->index()); - add_line(lines, ¤tLine, 0, currentLanes->size(), currentLanes->size(), (*it)->index()); - } - // For existing columns, we always just continue straight down - // ^^ I don't know what that means anymore :( - - } - } - //Add your own parents - - // If we already did the first parent, don't do so again - if (!didFirst && currentLanes->size() < MAX_LANES && commit.nParents) { - PBGitLane *newLane = new PBGitLane(commit.parentShas); - currentLanes->push_back(newLane); - newPos = currentLanes->size(); - add_line(lines, ¤tLine, 0, newPos, newPos, newLane->index()); - } - - // Add all other parents - - // If we add at least one parent, we can go back a single column. - // This boolean will tell us if that happened - BOOL addedParent = NO; - - int parentIndex; - for (parentIndex = 1; parentIndex < commit.nParents; ++parentIndex) { - git_oid *parent = commit.parentShas + parentIndex; - int i = 0; - BOOL was_displayed = NO; - std::list::iterator it = currentLanes->begin(); - for (; it != currentLanes->end(); ++it) { - i++; - if ((*it)->isCommit(parent)) { - add_line(lines, ¤tLine, 0, i, newPos,(*it)->index()); - was_displayed = YES; - break; - } - } - if (was_displayed) - continue; - - if (currentLanes->size() >= MAX_LANES) - break; - - // Really add this parent - addedParent = YES; - PBGitLane *newLane = new PBGitLane(parent); - currentLanes->push_back(newLane); - add_line(lines, ¤tLine, 0, currentLanes->size(), newPos, newLane->index()); - } - - previous = [[PBGraphCellInfo alloc] initWithPosition:newPos andLines:lines]; - if (currentLine > maxLines) - NSLog(@"Number of lines: %i vs allocated: %i", currentLine, maxLines); - - previous.nLines = currentLine; - previous.sign = commit.sign; - - // If a parent was added, we have room to not indent. - if (addedParent) - previous.numColumns = currentLanes->size() - 1; - else - previous.numColumns = currentLanes->size(); - - // Update the current lane to point to the new parent - if (currentLane && commit.nParents > 0) - currentLane->setSha(commit.parentShas[0]); - else - currentLanes->remove(currentLane); - - delete previousLanes; - - pl = currentLanes; - commit.lineInfo = previous; -} - -- (void) finalize -{ - std::list *lanes = (std::list *)pl; - std::list::iterator it = lanes->begin(); - for (; it != lanes->end(); ++it) - delete *it; - - delete lanes; - - [super finalize]; -} -@end diff --git a/PBGitHistoryController.h b/PBGitHistoryController.h deleted file mode 100644 index fadb6cd7c..000000000 --- a/PBGitHistoryController.h +++ /dev/null @@ -1,65 +0,0 @@ -// -// PBGitHistoryView.h -// GitX -// -// Created by Pieter de Bie on 19-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import "PBGitCommit.h" -#import "PBGitTree.h" -#import "PBViewController.h" -#import "PBCollapsibleSplitView.h" - -@interface PBGitHistoryController : PBViewController { - IBOutlet NSSearchField *searchField; - IBOutlet NSArrayController* commitController; - IBOutlet NSTreeController* treeController; - IBOutlet NSOutlineView* fileBrowser; - IBOutlet NSTableView* commitList; - IBOutlet PBCollapsibleSplitView *historySplitView; - - IBOutlet id webView; - int selectedTab; - - PBGitTree* gitTree; - PBGitCommit* webCommit; - PBGitCommit* rawCommit; - PBGitCommit* realCommit; -} - -@property (assign) int selectedTab; -@property (retain) PBGitCommit *webCommit, *rawCommit; -@property (retain) PBGitTree* gitTree; -@property (readonly) NSArrayController *commitController; - -- (IBAction) setDetailedView: sender; -- (IBAction) setRawView: sender; -- (IBAction) setTreeView: sender; - -- (void) selectCommit: (NSString*) commit; -- (IBAction) refresh: sender; -- (IBAction) toggleQuickView: sender; -- (IBAction) openSelectedFile: sender; -- (void) updateQuicklookForce: (BOOL) force; - -// Context menu methods -- (NSMenu *)contextMenuForTreeView; -- (NSArray *)menuItemsForPaths:(NSArray *)paths; -- (void)showCommitsFromTree:(id)sender; -- (void)showInFinderAction:(id)sender; -- (void)openFilesAction:(id)sender; - -- (void) copyCommitInfo; - -- (BOOL) hasNonlinearPath; - -- (NSMenu *)tableColumnMenu; - -- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview; -- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex; -- (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset; -- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset; - -@end diff --git a/PBGitHistoryController.m b/PBGitHistoryController.m deleted file mode 100644 index a9fde362b..000000000 --- a/PBGitHistoryController.m +++ /dev/null @@ -1,324 +0,0 @@ -// -// PBGitHistoryView.m -// GitX -// -// Created by Pieter de Bie on 19-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitHistoryController.h" -#import "CWQuickLook.h" -#import "PBGitGrapher.h" -#import "PBGitRevisionCell.h" -#import "PBCommitList.h" -#define QLPreviewPanel NSClassFromString(@"QLPreviewPanel") - - -@implementation PBGitHistoryController -@synthesize selectedTab, webCommit, rawCommit, gitTree, commitController; - -- (void)awakeFromNib -{ - self.selectedTab = [[NSUserDefaults standardUserDefaults] integerForKey:@"Repository Window Selected Tab Index"];; - [commitController addObserver:self forKeyPath:@"selection" options:(NSKeyValueObservingOptionNew,NSKeyValueObservingOptionOld) context:@"commitChange"]; - [treeController addObserver:self forKeyPath:@"selection" options:0 context:@"treeChange"]; - [repository addObserver:self forKeyPath:@"currentBranch" options:0 context:@"branchChange"]; - NSSize cellSpacing = [commitList intercellSpacing]; - cellSpacing.height = 0; - [commitList setIntercellSpacing:cellSpacing]; - [fileBrowser setTarget:self]; - [fileBrowser setDoubleAction:@selector(openSelectedFile:)]; - - if (!repository.currentBranch) { - [repository reloadRefs]; - [repository readCurrentBranch]; - } - else - [repository lazyReload]; - - // Set a sort descriptor for the subject column in the history list, as - // It can't be sorted by default (because it's bound to a PBGitCommit) - [[commitList tableColumnWithIdentifier:@"subject"] setSortDescriptorPrototype:[[NSSortDescriptor alloc] initWithKey:@"subject" ascending:YES]]; - // Add a menu that allows a user to select which columns to view - [[commitList headerView] setMenu:[self tableColumnMenu]]; - [historySplitView setTopMin:33.0 andBottomMin:100.0]; - [historySplitView uncollapse]; - [super awakeFromNib]; -} - -- (void) updateKeys -{ - NSArray* selection = [commitController selectedObjects]; - - // Remove any references in the QLPanel - //[[QLPreviewPanel sharedPreviewPanel] setURLs:[NSArray array] currentIndex:0 preservingDisplayState:YES]; - // We have to do this manually, as NSTreeController leaks memory? - //[treeController setSelectionIndexPaths:[NSArray array]]; - - if ([selection count] > 0) - realCommit = [selection objectAtIndex:0]; - else - realCommit = nil; - - self.webCommit = nil; - self.rawCommit = nil; - self.gitTree = nil; - - switch (self.selectedTab) { - case 0: self.webCommit = realCommit; break; - case 1: self.gitTree = realCommit.tree; break; - } -} - - -- (void) setSelectedTab: (int) number -{ - selectedTab = number; - [[NSUserDefaults standardUserDefaults] setInteger:selectedTab forKey:@"Repository Window Selected Tab Index"]; - [self updateKeys]; -} - -- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if ([(NSString *)context isEqualToString: @"commitChange"]) { - [self updateKeys]; - return; - } - else if ([(NSString *)context isEqualToString: @"treeChange"]) { - [self updateQuicklookForce: NO]; - } - else if([(NSString *)context isEqualToString:@"branchChange"]) { - // Reset the sorting - commitController.sortDescriptors = [NSArray array]; - } - else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - -- (IBAction) openSelectedFile: sender -{ - NSArray* selectedFiles = [treeController selectedObjects]; - if ([selectedFiles count] == 0) - return; - PBGitTree* tree = [selectedFiles objectAtIndex:0]; - NSString* name = [tree tmpFileNameForContents]; - [[NSWorkspace sharedWorkspace] openTempFile:name]; -} - -- (IBAction) setDetailedView: sender { - self.selectedTab = 0; -} -- (IBAction) setRawView: sender { - self.selectedTab = 1; -} -- (IBAction) setTreeView: sender { - self.selectedTab = 2; -} - -- (void)keyDown:(NSEvent*)event -{ - if ([[event charactersIgnoringModifiers] isEqualToString: @"f"] && [event modifierFlags] & NSAlternateKeyMask && [event modifierFlags] & NSCommandKeyMask) - [superController.window makeFirstResponder: searchField]; - else - [super keyDown: event]; -} - -- (void) copyCommitInfo -{ - PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0]; - if (!commit) - return; - NSString *info = [NSString stringWithFormat:@"%@ (%@)", [[commit realSha] substringToIndex:10], [commit subject]]; - - NSPasteboard *a =[NSPasteboard generalPasteboard]; - [a declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; - [a setString:info forType: NSStringPboardType]; - -} - -- (IBAction) toggleQuickView: sender -{ - id panel = [QLPreviewPanel sharedPreviewPanel]; - if ([panel isOpen]) { - [panel closePanel]; - } else { - [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFrontWithEffect:1]; - [self updateQuicklookForce: YES]; - } -} - -- (void) updateQuicklookForce: (BOOL) force -{ - if (!force && ![[QLPreviewPanel sharedPreviewPanel] isOpen]) - return; - - NSArray* selectedFiles = [treeController selectedObjects]; - - if ([selectedFiles count] == 0) - return; - - NSMutableArray* fileNames = [NSMutableArray array]; - for (PBGitTree* tree in selectedFiles) { - NSString* s = [tree tmpFileNameForContents]; - if (s) - [fileNames addObject:[NSURL fileURLWithPath: s]]; - } - - [[QLPreviewPanel sharedPreviewPanel] setURLs:fileNames currentIndex:0 preservingDisplayState:YES]; - -} - -- (IBAction) refresh: sender -{ - [repository reloadRefs]; - [repository.revisionList reload]; -} - -- (void) updateView -{ - [self refresh:nil]; -} - -- (NSResponder *)firstResponder; -{ - return commitList; -} - -- (void) selectCommit: (NSString*) commit -{ - NSPredicate* selection = [NSPredicate predicateWithFormat:@"realSha == %@", commit]; - NSArray* selectedCommits = [repository.revisionList.commits filteredArrayUsingPredicate:selection]; - [commitController setSelectedObjects: selectedCommits]; - int index = [[commitController selectionIndexes] firstIndex]; - [commitList scrollRowToVisible: index]; -} - -- (BOOL) hasNonlinearPath -{ - return [commitController filterPredicate] || [[commitController sortDescriptors] count] > 0; -} - -- (void) removeView -{ - [webView close]; - [commitController removeObserver:self forKeyPath:@"selection"]; - [treeController removeObserver:self forKeyPath:@"selection"]; - [repository removeObserver:self forKeyPath:@"currentBranch"]; - - [super removeView]; -} - -#pragma mark Table Column Methods -- (NSMenu *)tableColumnMenu -{ - NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Table columns menu"]; - for (NSTableColumn *column in [commitList tableColumns]) { - NSMenuItem *item = [[NSMenuItem alloc] init]; - [item setTitle:[[column headerCell] stringValue]]; - [item bind:@"value" - toObject:column - withKeyPath:@"hidden" - options:[NSDictionary dictionaryWithObject:@"NSNegateBoolean" forKey:NSValueTransformerNameBindingOption]]; - [menu addItem:item]; - } - return menu; -} - -#pragma mark Tree Context Menu Methods - -- (void)showCommitsFromTree:(id)sender -{ - // TODO: Enable this from webview as well! - - NSMutableArray *filePaths = [NSMutableArray arrayWithObjects:@"HEAD", @"--", NULL]; - [filePaths addObjectsFromArray:[sender representedObject]]; - - PBGitRevSpecifier *revSpec = [[PBGitRevSpecifier alloc] initWithParameters:filePaths]; - - repository.currentBranch = [repository addBranch:revSpec]; -} - -- (void)showInFinderAction:(id)sender -{ - NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"]; - NSString *path; - NSWorkspace *ws = [NSWorkspace sharedWorkspace]; - - for (NSString *filePath in [sender representedObject]) { - path = [workingDirectory stringByAppendingPathComponent:filePath]; - [ws selectFile: path inFileViewerRootedAtPath:path]; - } - -} - -- (void)openFilesAction:(id)sender -{ - NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"]; - NSString *path; - NSWorkspace *ws = [NSWorkspace sharedWorkspace]; - - for (NSString *filePath in [sender representedObject]) { - path = [workingDirectory stringByAppendingPathComponent:filePath]; - [ws openFile:path]; - } -} - - -- (NSMenu *)contextMenuForTreeView -{ - NSArray *filePaths = [[treeController selectedObjects] valueForKey:@"fullPath"]; - - NSMenu *menu = [[NSMenu alloc] init]; - for (NSMenuItem *item in [self menuItemsForPaths:filePaths]) - [menu addItem:item]; - return menu; -} - -- (NSArray *)menuItemsForPaths:(NSArray *)paths -{ - BOOL multiple = [paths count] != 1; - NSMenuItem *historyItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Show history of files" : @"Show history of file" - action:@selector(showCommitsFromTree:) - keyEquivalent:@""]; - NSMenuItem *finderItem = [[NSMenuItem alloc] initWithTitle:@"Show in Finder" - action:@selector(showInFinderAction:) - keyEquivalent:@""]; - NSMenuItem *openFilesItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Open Files" : @"Open File" - action:@selector(openFilesAction:) - keyEquivalent:@""]; - - NSArray *menuItems = [NSArray arrayWithObjects:historyItem, finderItem, openFilesItem, nil]; - for (NSMenuItem *item in menuItems) { - [item setTarget:self]; - [item setRepresentedObject:paths]; - } - - return menuItems; -} - -- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview { - return TRUE; -} - -- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex { - int index = [[splitView subviews] indexOfObject:subview]; - // this method (and canCollapse) are called by the splitView to decide how to collapse on double-click - // we compare our two subviews, so that always the smaller one is collapsed. - if([[[splitView subviews] objectAtIndex:index] frame].size.height < [[[splitView subviews] objectAtIndex:((index+1)%2)] frame].size.height) { - return TRUE; - } - return FALSE; -} - -- (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset { - return proposedMin + historySplitView.topViewMin; -} - -- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset { - if(offset == 1) - return proposedMax - historySplitView.bottomViewMin; - return [sender frame].size.height; -} - -@end diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib deleted file mode 100644 index 68b7789eb..000000000 --- a/PBGitHistoryView.xib +++ /dev/null @@ -1,3129 +0,0 @@ - - - - 1050 - 9L31a - 677 - 949.54 - 353.00 - - YES - - - - - YES - com.apple.WebKitIBPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - - YES - - PBGitHistoryController - - - FirstResponder - - - NSApplication - - - - YES - path - contents - selectedTab - textContents - - PBGitTree - - YES - YES - YES - children - leaf - - - - YES - self - sha - details - subject - @count - self.@count - author - children - tree - tree.children - selection.tree.children - treeCon - treeContents - treeChildren - tree.s - - commits.@max.tree.children - - authorDate - date - dateString - arran - realSha - - PBGitCommit - - YES - YES - YES - YES - - - PBWebHistoryController - - - - 274 - - YES - - - 293 - {{376, 6}, {71, 25}} - - YES - - -2080244224 - 0 - - LucidaGrande - 1.300000e+01 - 1044 - - - - YES - - 3.200000e+01 - - NSImage - DetailViewTemplate - - - Detailed View - YES - 2 - - - - NSImage - NSPathTemplate - - - Tree View - 9 - 2 - - - 2 - - - - - 274 - - YES - - - 4370 - - YES - - - 2304 - - YES - - - 256 - {852, 194} - - YES - - - 256 - {852, 17} - - - - - - -2147483392 - {{-26, 0}, {16, 17}} - - - - YES - - SubjectColumn - 5.090000e+02 - 4.000000e+01 - 1.000000e+03 - - 75628032 - 0 - Subject - - LucidaGrande - 1.100000e+01 - 3100 - - - 3 - MC4zMzMzMzI5OQA - - - 6 - System - headerTextColor - - 3 - MAA - - - - - 337772096 - 2048 - Text Cell - - - - 6 - System - controlBackgroundColor - - 3 - MC42NjY2NjY2OQA - - - - 6 - System - controlTextColor - - - - 3 - YES - - - - AuthorColumn - 1.900000e+02 - 4.000000e+01 - 1.000000e+03 - - 75628032 - 0 - Author - - - - - - 337772096 - 2048 - Text Cell - - - - - - 3 - YES - - - - DateColumn - 1.440000e+02 - 1.000000e+01 - 3.402823e+38 - - 75628032 - 0 - Date - - - 6 - System - headerColor - - 3 - MQA - - - - - - 337772096 - 2048 - Text Cell - - - - - - 3 - YES - - - - SHAColumn - 6.400000e+01 - 1.000000e+01 - 3.402823e+38 - - 75628032 - 0 - SHA - - - - - - 338820672 - 1024 - Text Cell - - - - - - 3 - YES - - YES - - - 3.000000e+00 - 2.000000e+00 - - - 6 - System - gridColor - - 3 - MC41AA - - - 1.700000e+01 - -683671552 - CommitView - 5 - 15 - 0 - YES - - - {{0, 17}, {852, 194}} - - - - - 2 - - - - -2147483392 - {{837, 17}, {15, 179}} - - - _doScroller: - 3.700000e+01 - 1.947368e-01 - - - - -2147483392 - {{0, 196}, {837, 15}} - - 1 - - _doScroller: - 2.193211e-01 - - - - 2304 - - YES - - - {852, 17} - - - - - 4 - - - - {852, 211} - - - 560 - - - - - - QSAAAEEgAABBmAAAQZgAAA - - - - 18 - {{0, 212}, {852, 186}} - - - YES - - 1 - - - 274 - - YES - - - 274 - - YES - - YES - Apple HTML pasteboard type - Apple PDF pasteboard type - Apple PICT pasteboard type - Apple URL pasteboard type - Apple Web Archive pasteboard type - NSColor pasteboard type - NSFilenamesPboardType - NSStringPboardType - NeXT RTFD pasteboard type - NeXT Rich Text Format v1.0 pasteboard type - NeXT TIFF v4.0 pasteboard type - WebURLsWithTitlesPboardType - public.png - public.url - public.url-name - - - {{1, 0}, {851, 186}} - - - - - - - - YES - - YES - WebKitDefaultFixedFontSize - WebKitDefaultFontSize - WebKitMinimumFontSize - - - YES - - - - - - - YES - YES - - - {852, 186} - - - Details - - 6 - System - controlColor - - - - - - Item 2 - - - 256 - - YES - - - 274 - - YES - - - 276 - - YES - - - 2304 - - YES - - - 256 - {191, 186} - - YES - - - 256 - {{223, 0}, {16, 17}} - - - YES - - 1.880000e+02 - 1.600000e+01 - 1.000000e+03 - - 75628032 - 0 - - - - 3 - MC4zMzMzMzI5OQA - - - - - 337772096 - 2048 - Text Cell - - - - - - 3 - YES - - - - 3.000000e+00 - 2.000000e+00 - - - 1.700000e+01 - -624951296 - 4 - 15 - 0 - YES - - - {{1, 1}, {191, 186}} - - - - - 4 - - - - 256 - {{192, 1}, {15, 186}} - - - _doScroller: - 9.948186e-01 - - - - 256 - {{-100, -100}, {502, 15}} - - 1 - - _doScroller: - 4.504505e-03 - 9.980119e-01 - - - {208, 188} - - - 18 - - - - QSAAAEEgAABBmAAAQZgAAA - - - - 274 - - YES - - - 2304 - - YES - - - 2322 - {543, 112} - - - - - - Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum Et harumd und lookum like Greek to me, dereud facilis est er expedit distinct. Nam liber te conscient to factor tum poen legum odioque civiuda - - - YES - - YES - NSFont - NSParagraphStyle - - - YES - - Monaco - 1.000000e+01 - 16 - - - 3 - - YES - - 0.000000e+00 - - - 5.600000e+01 - - - 1.120000e+02 - - - 1.680000e+02 - - - 2.240000e+02 - - - 2.800000e+02 - - - 3.360000e+02 - - - 3.920000e+02 - - - 4.480000e+02 - - - 5.040000e+02 - - - 5.600000e+02 - - - 6.160000e+02 - - - 6.720000e+02 - - - 7.280000e+02 - - - 7.840000e+02 - - - 8.400000e+02 - - - 8.960000e+02 - - - 9.520000e+02 - - - 1.008000e+03 - - - 1.064000e+03 - - - 1.120000e+03 - - - 1.176000e+03 - - - 1.232000e+03 - - - 1.288000e+03 - - - 1.344000e+03 - - - 1.400000e+03 - - - 1.456000e+03 - - - 1.512000e+03 - - - 1.568000e+03 - - - 1.624000e+03 - - - 1.680000e+03 - - - 1.736000e+03 - - - - - - - - - YES - - - 6 - - - - 5.430000e+02 - 1 - - - 11233 - - - - YES - - YES - NSBackgroundColor - NSColor - - - YES - - 6 - System - selectedTextBackgroundColor - - - - 6 - System - selectedTextColor - - - - - - - YES - - YES - NSColor - NSUnderline - - - YES - - 1 - MCAwIDEAA - - - - - - - 6 - {1186, 1e+07} - {0, 0} - - - - {{1, 1}, {626, 186}} - - - - - - {4, -5} - 1 - - 4 - - - - 256 - {{627, 1}, {15, 186}} - - - _doScroller: - 3.003168e-02 - - - - 256 - {{-100, -100}, {87, 18}} - - 1 - - _doScroller: - 1.000000e+00 - 9.456522e-01 - - - {{209, 0}, {643, 188}} - - - 18 - - - - - - {852, 188} - - YES - 2 - - - {852, 186} - - Tree - - - - - - - 6 - YES - YES - - YES - - - - - {{0, 35}, {852, 398}} - - 2 - HistoryViewSplitView - - - - 292 - {{17, 7}, {305, 17}} - - YES - - 67239488 - 272630784 - Label - - - - - - - - - 289 - {{795, 6}, {37, 25}} - - YES - - -2080244224 - 134217728 - Textured Button - - - -2033958657 - 163 - - NSImage - NSQuickLookTemplate - - - - 400 - 75 - - - - {852, 432} - - NSView - - - PBRefController - - - 15 - 2 - {{196, 408}, {346, 102}} - 603979776 - New Branch Sheet - NSWindow - - {1000, 102} - {346, 102} - - - 256 - - YES - - - 266 - {{177, 60}, {149, 22}} - - YES - - -1804468671 - 272630784 - - - topic - - YES - - 6 - System - textBackgroundColor - - - - 6 - System - textColor - - - - - - - 268 - {{17, 62}, {155, 17}} - - YES - - 68288064 - 272630784 - Create a branch named: - - - - - - - - - 289 - {{236, 12}, {96, 32}} - - YES - - 67239424 - 134217728 - Create - - - -2038284033 - 129 - - DQ - 200 - 25 - - - - - 289 - {{140, 12}, {96, 32}} - - YES - - 67239424 - 134217728 - Cancel - - - -2038284033 - 129 - - Gw - 200 - 25 - - - - - 292 - {{17, 22}, {85, 17}} - - YES - - 68288064 - 272630784 - Invalid name - - - - - 1 - MSAwIDAAA - - - - - {346, 102} - - - {{0, 0}, {1680, 1028}} - {346, 124} - {1000, 124} - - - - FFA3AADE-2DC8-4306-B161-4916009C1071 - - - YES - YES - YES - YES - 1 - 1 - - YES - - YES - 1E431E79-1591-49E7-9E17-49497CA4622A - 7FFB691C-2D2F-49A9-997F-AE1AE8BFF3F1 - 86360841-A2B1-4802-845A-AE424521FE99 - 99C2C9EB-AE16-42A9-BE52-46CE903E9AF9 - NSToolbarFlexibleSpaceItem - NSToolbarSeparatorItem - - - YES - - - 1E431E79-1591-49E7-9E17-49497CA4622A - - View - View selector - - - - 268 - {{0, 14}, {87, 25}} - 3 - YES - - 67239424 - 0 - - - - YES - - 4.000000e+01 - - NSImage - HistoryViewTemplate - - - History View - 0 - - - 4.000000e+01 - - NSImage - CommitViewTemplate - - - Commit View - YES - 0 - - - 1 - 2 - - - - - - {87, 25} - {87, 25} - YES - YES - 3 - YES - 0 - - - - 7FFB691C-2D2F-49A9-997F-AE1AE8BFF3F1 - - Search - - Search Field - - - - - 265 - {{0, 14}, {183, 22}} - YES - - 343014976 - 268436480 - - Subject - - YES - 1 - - - - 130560 - 0 - search - _searchFieldSearch: - - - 138690815 - 0 - - 400 - 75 - - - 130560 - 0 - clear - - YES - - YES - - YES - AXDescription - NSAccessibilityEncodedAttributesValueType - - - YES - cancel - - - - - _searchFieldCancel: - - - 138690815 - 0 - - 400 - 75 - - 255 - CAAAAA - - - - - - {183, 22} - {183, 22} - YES - YES - 0 - YES - 0 - - - - 86360841-A2B1-4802-845A-AE424521FE99 - - Create Branch - Create Branch - - - - 268 - {{21, 14}, {40, 25}} - YES - - -2080244224 - 134217728 - - - - -2033434369 - 163 - - NSImage - AddBranchTemplate - - - - 400 - 75 - - - - - - {40, 25} - {40, 25} - YES - YES - 0 - YES - 0 - - - - 99C2C9EB-AE16-42A9-BE52-46CE903E9AF9 - - Branch - Branch - - - - 268 - {{0, 14}, {134, 26}} - YES - - -2076049856 - 2048 - - - 109199615 - 1 - - - 400 - 75 - - - master - - 1048576 - 2147483647 - 1 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - _popUpItemAction: - - - YES - - OtherViews - - YES - - - - Item 2 - - 1048576 - 2147483647 - - - _popUpItemAction: - - - - - Item 3 - - 1048576 - 2147483647 - - - _popUpItemAction: - - - - - 1 - YES - YES - 2 - - - - - - {134, 25} - {134, 25} - YES - YES - 0 - YES - 0 - - - NSToolbarFlexibleSpaceItem - - Flexible Space - - - - - - {1, 5} - {20000, 32} - YES - YES - -1 - YES - 0 - - YES - YES - - - 1048576 - 2147483647 - - - - - - NSToolbarSeparatorItem - - Separator - - - - - - {12, 5} - {12, 1000} - YES - YES - -1 - YES - 0 - - YES - YES - - - 1048576 - 2147483647 - - - - - - - - YES - - - - - - - - - YES - - - - - - - - - YES - - - - - - YES - - - webView - - - - 37 - - - - treeController - - - - 41 - - - - commitController - - - - 42 - - - - commitList - - - - 43 - - - - view - - - - 44 - - - - view - - - - 53 - - - - value: arrangedObjects.author - - - - - - value: arrangedObjects.author - value - arrangedObjects.author - - NSConditionallySetsEditable - - - 2 - - - 55 - - - - contentArray: repository.revisionList.commits - - - - - - contentArray: repository.revisionList.commits - contentArray - repository.revisionList.commits - 2 - - - 58 - - - - value: arrangedObjects - - - - - - value: arrangedObjects - value - arrangedObjects - - NSConditionallySetsEditable - - - 2 - - - 61 - - - - historyController - - - - 62 - - - - toggleQuickView: - - - - 66 - - - - selectedIndex: selectedTab - - - - - - selectedIndex: selectedTab - selectedIndex - selectedTab - 2 - - - 80 - - - - selectedIndex: selectedTab - - - - - - selectedIndex: selectedTab - selectedIndex - selectedTab - 2 - - - 83 - - - - frameLoadDelegate - - - - 84 - - - - UIDelegate - - - - 85 - - - - controller - - - - 86 - - - - fileBrowser - - - - 87 - - - - contentArray: gitTree.children - - - - - - contentArray: gitTree.children - contentArray - gitTree.children - 2 - - - 90 - - - - value: arrangedObjects.path - - - - - - value: arrangedObjects.path - value - arrangedObjects.path - - NSConditionallySetsEditable - - - 2 - - - 92 - - - - webController - - - - 95 - - - - delegate - - - - 96 - - - - value: arrangedObjects.dateString - - - - - - value: arrangedObjects.dateString - value - arrangedObjects.dateString - - NSConditionallySetsEditable - - - 2 - - - 98 - - - - displayPatternValue1: arrangedObjects.@count - - - - - - displayPatternValue1: arrangedObjects.@count - displayPatternValue1 - arrangedObjects.@count - - NSDisplayPattern - %{value1}@ commits loaded - - 2 - - - 106 - - - - delegate - - - - 107 - - - - policyDelegate - - - - 109 - - - - controller - - - - 110 - - - - controller - - - - 111 - - - - webView - - - - 112 - - - - viewToolbar - - - - 160 - - - - searchField - - - - 185 - - - - predicate: filterPredicate - - - - - - predicate: filterPredicate - predicate - filterPredicate - - YES - - YES - NSDisplayName - NSPredicateFormat - - - YES - Subject - subject contains[c] $value - - - 2 - - - 214 - - - - predicate2: filterPredicate - - - - - - predicate2: filterPredicate - predicate2 - filterPredicate - - YES - - YES - NSDisplayName - NSPredicateFormat - - - YES - Author - author contains[c] $value - - - - 2 - - - 215 - - - - controller - - - - 217 - - - - commitController - - - - 232 - - - - commitList - - - - 233 - - - - historyController - - - - 234 - - - - dataSource - - - - 235 - - - - performClick: - - - - 248 - - - - newBranchSheet - - - - 249 - - - - newBranchName - - - - 250 - - - - closeSheet: - - - - 252 - - - - saveSheet: - - - - 253 - - - - contextMenuDelegate - - - - 259 - - - - contextMenuDelegate - - - - 260 - - - - addRef: - - - - 264 - - - - branchPopUp - - - - 268 - - - - predicate3: filterPredicate - - - - - - predicate3: filterPredicate - predicate3 - filterPredicate - - YES - - YES - NSDisplayName - NSPredicateFormat - - - YES - SHA - realSha contains $value - - - - 2 - - - 271 - - - - errorMessage - - - - 274 - - - - historySplitView - - - - 275 - - - - value: arrangedObjects.realSha - - - - - - value: arrangedObjects.realSha - value - arrangedObjects.realSha - 2 - - - 290 - - - - value: selection.textContents - - - - - - value: selection.textContents - value - selection.textContents - - YES - - YES - NSAllowsEditingMultipleValuesSelection - NSConditionallySetsEditable - - - YES - - - - - 2 - - - 291 - - - - - YES - - 0 - - YES - - - - - - -2 - - - RmlsZSdzIE93bmVyA - - - -1 - - - First Responder - - - -3 - - - Application - - - 38 - - - - - 39 - - - CommitsController - - - 40 - - - - - 46 - - - YES - - - - - - - - - 2 - - - YES - - - - - History View - - - 4 - - - YES - - - - - Bottom View - - - 6 - - - YES - - - - Web View - - - 7 - - - YES - - - - - - 8 - - - YES - - - - - - 9 - - - YES - - - - - - - 10 - - - YES - - - - - - - - 11 - - - YES - - - - - - - - 12 - - - - - 13 - - - - - 14 - - - - - 15 - - - YES - - - - - - 16 - - - - - 17 - - - - - 18 - - - YES - - - - - - 19 - - - - - 20 - - - YES - - - - - - 21 - - - - - 47 - - - YES - - - - - - 48 - - - YES - - - - - - 49 - - - YES - - - - - - 50 - - - - - 51 - - - - - 52 - - - - - 231 - - - - - 236 - - - YES - - - - - - 237 - - - YES - - - - - - - - - - 238 - - - YES - - - - - - 239 - - - YES - - - - - - 240 - - - YES - - - - - - 242 - - - YES - - - - - - 243 - - - - - 245 - - - - - 246 - - - - - 247 - - - - - 3 - - - YES - - - - - - - - - 27 - - - YES - - - - - - - Commit List - - - 31 - - - YES - - - - - - 36 - - - - - 32 - - - YES - - - - - - 35 - - - - - 33 - - - YES - - - - - - 34 - - - - - 28 - - - - - 29 - - - - - 30 - - - - - 113 - - - YES - - - - - - - - - History Toolbar - - - 115 - - - - - 116 - - - YES - - - - - - 222 - - - - - 223 - - - YES - - - - - - 263 - - - YES - - - - - - 261 - - - YES - - - - - - 262 - - - - - 224 - - - YES - - - - - - 225 - - - - - 117 - - - YES - - - - - - 118 - - - - - 114 - - - YES - - - - - - 119 - - - YES - - - - - - 120 - - - YES - - - - - - 121 - - - YES - - - - - - - - 124 - - - - - 123 - - - - - 122 - - - - - 272 - - - YES - - - - - - 273 - - - - - 287 - - - YES - - - - - - 288 - - - - - - - YES - - YES - -1.IBPluginDependency - -2.IBPluginDependency - -3.IBPluginDependency - 10.IBPluginDependency - 11.IBPluginDependency - 113.IBEditorWindowLastContentRect - 113.IBPluginDependency - 113.editorWindowContentRectSynchronizationRect - 114.IBPluginDependency - 116.IBPluginDependency - 117.IBPluginDependency - 118.IBPluginDependency - 119.IBPluginDependency - 12.IBPluginDependency - 120.IBPluginDependency - 121.IBEditorWindowLastContentRect - 121.IBPluginDependency - 121.editorWindowContentRectSynchronizationRect - 122.IBPluginDependency - 123.IBPluginDependency - 124.IBPluginDependency - 13.IBPluginDependency - 14.IBPluginDependency - 15.CustomClassName - 15.IBPluginDependency - 16.IBPluginDependency - 17.IBPluginDependency - 18.IBPluginDependency - 19.IBPluginDependency - 2.CustomClassName - 2.IBEditorWindowLastContentRect - 2.IBPluginDependency - 2.ImportedFromIB2 - 20.IBPluginDependency - 21.IBAttributePlaceholdersKey - 21.IBPluginDependency - 223.IBPluginDependency - 224.IBPluginDependency - 225.IBPluginDependency - 231.IBPluginDependency - 236.IBEditorWindowLastContentRect - 236.IBWindowTemplateEditedContentRect - 236.NSWindowTemplate.visibleAtLaunch - 236.windowTemplate.hasMaxSize - 236.windowTemplate.hasMinSize - 236.windowTemplate.maxSize - 236.windowTemplate.minSize - 237.IBPluginDependency - 238.IBPluginDependency - 239.IBPluginDependency - 240.IBPluginDependency - 242.IBPluginDependency - 243.IBPluginDependency - 245.IBPluginDependency - 246.IBPluginDependency - 247.IBPluginDependency - 261.IBAttributePlaceholdersKey - 261.IBPluginDependency - 262.IBPluginDependency - 263.IBPluginDependency - 27.CustomClassName - 27.IBPluginDependency - 27.IBViewIntegration.shadowBlurRadius - 27.IBViewIntegration.shadowColor - 27.IBViewIntegration.shadowOffsetHeight - 27.IBViewIntegration.shadowOffsetWidth - 27.ImportedFromIB2 - 272.IBPluginDependency - 273.IBPluginDependency - 28.IBPluginDependency - 28.IBShouldRemoveOnLegacySave - 287.IBPluginDependency - 288.IBPluginDependency - 29.IBPluginDependency - 29.IBShouldRemoveOnLegacySave - 3.IBPluginDependency - 3.ImportedFromIB2 - 30.CustomClassName - 30.IBPluginDependency - 30.IBShouldRemoveOnLegacySave - 31.IBPluginDependency - 31.ImportedFromIB2 - 32.IBPluginDependency - 32.ImportedFromIB2 - 33.IBPluginDependency - 34.IBPluginDependency - 35.CustomClassName - 35.IBPluginDependency - 35.ImportedFromIB2 - 36.IBPluginDependency - 36.ImportedFromIB2 - 38.IBPluginDependency - 39.IBPluginDependency - 39.ImportedFromIB2 - 4.IBAttributePlaceholdersKey - 4.IBPluginDependency - 40.IBPluginDependency - 46.IBEditorWindowLastContentRect - 46.IBPluginDependency - 47.IBPluginDependency - 48.IBPluginDependency - 49.IBPluginDependency - 50.IBPluginDependency - 51.IBPluginDependency - 52.IBPluginDependency - 6.IBPluginDependency - 7.IBPluginDependency - 8.IBPluginDependency - 9.IBPluginDependency - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilderKit - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{312, 366}, {616, 227}} - com.apple.InterfaceBuilder.CocoaPlugin - {{132, 614}, {616, 0}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{725, 616}, {134, 63}} - com.apple.InterfaceBuilder.CocoaPlugin - {{848, 458}, {116, 63}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBQLOutlineView - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBCollapsibleSplitView - {{312, 577}, {852, 384}} - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - YES - - YES - - - YES - - - com.apple.WebKitIBPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{504, 612}, {346, 102}} - {{504, 612}, {346, 102}} - - - - {1000, 102} - {346, 102} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Create Branch - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBCommitList - com.apple.InterfaceBuilder.CocoaPlugin - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - PBUnsortableTableHeader - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - PBGitRevisionCell - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - YES - - YES - - - YES - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{321, 67}, {852, 432}} - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 291 - - - - YES - - PBCollapsibleSplitView - PBNiceSplitView - - IBProjectSource - PBCollapsibleSplitView.h - - - - PBCommitList - NSTableView - - YES - - YES - controller - webController - webView - - - YES - PBGitHistoryController - PBWebHistoryController - WebView - - - - IBProjectSource - PBCommitList.h - - - - PBGitHistoryController - PBViewController - - YES - - YES - openFilesAction: - openSelectedFile: - refresh: - setDetailedView: - setRawView: - setTreeView: - showCommitsFromTree: - showInFinderAction: - toggleQuickView: - - - YES - id - id - id - id - id - id - id - id - id - - - - YES - - YES - commitController - commitList - fileBrowser - historySplitView - searchField - treeController - webView - - - YES - NSArrayController - NSTableView - NSOutlineView - PBCollapsibleSplitView - NSSearchField - NSTreeController - id - - - - IBProjectSource - PBGitHistoryController.h - - - - PBGitHistoryController - PBViewController - - IBUserSource - - - - - PBGitRevisionCell - NSActionCell - - YES - - YES - contextMenuDelegate - controller - - - YES - id - PBGitHistoryController - - - - IBProjectSource - PBGitRevisionCell.h - - - - PBNiceSplitView - NSSplitView - - IBProjectSource - PBNiceSplitView.h - - - - PBQLOutlineView - NSOutlineView - - controller - PBGitHistoryController - - - IBProjectSource - PBQLOutlineView.h - - - - PBRefController - NSObject - - YES - - YES - addRef: - changeBranch: - closeSheet: - saveSheet: - - - YES - id - NSMenuItem - id - id - - - - YES - - YES - branchPopUp - commitController - commitList - errorMessage - historyController - newBranchName - newBranchSheet - - - YES - NSPopUpButton - NSArrayController - PBCommitList - NSTextField - PBGitHistoryController - NSTextField - NSWindow - - - - IBProjectSource - PBRefController.h - - - - PBUnsortableTableHeader - NSTableHeaderView - - controller - NSArrayController - - - IBProjectSource - PBUnsortableTableHeader.h - - - - PBViewController - NSViewController - - viewToolbar - NSToolbar - - - IBProjectSource - PBViewController.h - - - - PBWebController - NSObject - - YES - - YES - repository - view - - - YES - id - WebView - - - - IBProjectSource - PBWebController.h - - - - PBWebHistoryController - PBWebController - - YES - - YES - contextMenuDelegate - historyController - - - YES - id - PBGitHistoryController - - - - IBProjectSource - PBWebHistoryController.h - - - - PBWebHistoryController - PBWebController - - view - WebView - - - IBUserSource - - - - - - 0 - GitX.xcodeproj - 3 - - diff --git a/PBGitIndex.m b/PBGitIndex.m deleted file mode 100644 index bd9bd561a..000000000 --- a/PBGitIndex.m +++ /dev/null @@ -1,632 +0,0 @@ -// -// PBGitIndex.m -// GitX -// -// Created by Pieter de Bie on 9/12/09. -// Copyright 2009 Pieter de Bie. All rights reserved. -// - -#import "PBGitIndex.h" -#import "PBGitRepository.h" -#import "PBGitBinary.h" -#import "PBEasyPipe.h" -#import "NSString_RegEx.h" -#import "PBChangedFile.h" - -NSString *PBGitIndexIndexRefreshStatus = @"PBGitIndexIndexRefreshStatus"; -NSString *PBGitIndexIndexRefreshFailed = @"PBGitIndexIndexRefreshFailed"; -NSString *PBGitIndexFinishedIndexRefresh = @"PBGitIndexFinishedIndexRefresh"; - -NSString *PBGitIndexIndexUpdated = @"GBGitIndexIndexUpdated"; - -NSString *PBGitIndexCommitStatus = @"PBGitIndexCommitStatus"; -NSString *PBGitIndexCommitFailed = @"PBGitIndexCommitFailed"; -NSString *PBGitIndexFinishedCommit = @"PBGitIndexFinishedCommit"; - -NSString *PBGitIndexAmendMessageAvailable = @"PBGitIndexAmendMessageAvailable"; -NSString *PBGitIndexOperationFailed = @"PBGitIndexOperationFailed"; - -@interface PBGitIndex (IndexRefreshMethods) - -- (NSArray *)linesFromNotification:(NSNotification *)notification; -- (NSMutableDictionary *)dictionaryForLines:(NSArray *)lines; -- (void)addFilesFromDictionary:(NSMutableDictionary *)dictionary staged:(BOOL)staged tracked:(BOOL)tracked; - -- (void)indexStepComplete; - -- (void)indexRefreshFinished:(NSNotification *)notification; -- (void)readOtherFiles:(NSNotification *)notification; -- (void)readUnstagedFiles:(NSNotification *)notification; -- (void)readStagedFiles:(NSNotification *)notification; - -@end - -@interface PBGitIndex () - -// Returns the tree to compare the index to, based -// on whether amend is set or not. -- (NSString *) parentTree; -- (void)postCommitUpdate:(NSString *)update; -- (void)postCommitFailure:(NSString *)reason; -- (void)postIndexChange; -- (void)postOperationFailed:(NSString *)description; -@end - -@implementation PBGitIndex - -@synthesize amend; - -- (id)initWithRepository:(PBGitRepository *)theRepository workingDirectory:(NSURL *)theWorkingDirectory -{ - if (!(self = [super init])) - return nil; - - NSAssert(theWorkingDirectory, @"PBGitIndex requires a working directory"); - NSAssert(theRepository, @"PBGitIndex requires a repository"); - - repository = theRepository; - workingDirectory = theWorkingDirectory; - files = [NSMutableArray array]; - - return self; -} - -- (NSArray *)indexChanges -{ - return files; -} - -- (void)setAmend:(BOOL)newAmend -{ - if (newAmend == amend) - return; - - amend = newAmend; - amendEnvironment = nil; - - [self refresh]; - - if (!newAmend) - return; - - // If we amend, we want to keep the author information for the previous commit - // We do this by reading in the previous commit, and storing the information - // in a dictionary. This dictionary will then later be read by [self commit:] - NSString *message = [repository outputForCommand:@"cat-file commit HEAD"]; - NSArray *match = [message substringsMatchingRegularExpression:@"\nauthor ([^\n]*) <([^\n>]*)> ([0-9]+[^\n]*)\n" count:3 options:0 ranges:nil error:nil]; - if (match) - amendEnvironment = [NSDictionary dictionaryWithObjectsAndKeys:[match objectAtIndex:1], @"GIT_AUTHOR_NAME", - [match objectAtIndex:2], @"GIT_AUTHOR_EMAIL", - [match objectAtIndex:3], @"GIT_AUTHOR_DATE", - nil]; - - // Find the commit message - NSRange r = [message rangeOfString:@"\n\n"]; - if (r.location != NSNotFound) { - NSString *commitMessage = [message substringFromIndex:r.location + 2]; - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexAmendMessageAvailable - object: self - userInfo:[NSDictionary dictionaryWithObject:commitMessage forKey:@"message"]]; - } - -} - -- (void)refresh -{ - // If we were already refreshing the index, we don't want - // double notifications. As we can't stop the tasks anymore, - // just cancel the notifications - refreshStatus = 0; - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:self]; - - // Ask Git to refresh the index - NSFileHandle *updateHandle = [PBEasyPipe handleForCommand:[PBGitBinary path] - withArgs:[NSArray arrayWithObjects:@"update-index", @"-q", @"--unmerged", @"--ignore-missing", @"--refresh", nil] - inDir:[workingDirectory path]]; - - [nc addObserver:self - selector:@selector(indexRefreshFinished:) - name:NSFileHandleReadToEndOfFileCompletionNotification - object:updateHandle]; - [updateHandle readToEndOfFileInBackgroundAndNotify]; - -} - -- (NSString *) parentTree -{ - NSString *parent = amend ? @"HEAD^" : @"HEAD"; - - if (![repository parseReference:parent]) - // We don't have a head ref. Return the empty tree. - return @"4b825dc642cb6eb9a060e54bf8d69288fbee4904"; - - return parent; -} - -// TODO: make Asynchronous -- (void)commitWithMessage:(NSString *)commitMessage -{ - NSMutableString *commitSubject = [@"commit: " mutableCopy]; - NSRange newLine = [commitMessage rangeOfString:@"\n"]; - if (newLine.location == NSNotFound) - [commitSubject appendString:commitMessage]; - else - [commitSubject appendString:[commitMessage substringToIndex:newLine.location]]; - - NSString *commitMessageFile; - commitMessageFile = [repository.fileURL.path stringByAppendingPathComponent:@"COMMIT_EDITMSG"]; - - [commitMessage writeToFile:commitMessageFile atomically:YES encoding:NSUTF8StringEncoding error:nil]; - - - [self postCommitUpdate:@"Creating tree"]; - NSString *tree = [repository outputForCommand:@"write-tree"]; - if ([tree length] != 40) - return [self postCommitFailure:@"Creating tree failed"]; - - - NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"commit-tree", tree, nil]; - NSString *parent = amend ? @"HEAD^" : @"HEAD"; - if ([repository parseReference:parent]) { - [arguments addObject:@"-p"]; - [arguments addObject:parent]; - } - - [self postCommitUpdate:@"Creating commit"]; - int ret = 1; - NSString *commit = [repository outputForArguments:arguments - inputString:commitMessage - byExtendingEnvironment:amendEnvironment - retValue: &ret]; - - if (ret || [commit length] != 40) - return [self postCommitFailure:@"Could not create a commit object"]; - - [self postCommitUpdate:@"Running hooks"]; - if (![repository executeHook:@"pre-commit" output:nil]) - return [self postCommitFailure:@"Pre-commit hook failed"]; - - if (![repository executeHook:@"commit-msg" withArgs:[NSArray arrayWithObject:commitMessageFile] output:nil]) - return [self postCommitFailure:@"Commit-msg hook failed"]; - - [self postCommitUpdate:@"Updating HEAD"]; - [repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-m", commitSubject, @"HEAD", commit, nil] - retValue: &ret]; - if (ret) - return [self postCommitFailure:@"Could not update HEAD"]; - - [self postCommitUpdate:@"Running post-commit hook"]; - - BOOL success = [repository executeHook:@"post-commit" output:nil]; - NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithBool:success] forKey:@"success"]; - NSString *description; - if (success) - description = [NSString stringWithFormat:@"Successfull created commit %@", commit]; - else - description = [NSString stringWithFormat:@"Post-commit hook failed, but successfully created commit %@", commit]; - - [userInfo setObject:description forKey:@"description"]; - [userInfo setObject:commit forKey:@"sha"]; - - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexFinishedCommit - object:self - userInfo:userInfo]; - if (!success) - return; - - repository.hasChanged = YES; - - amendEnvironment = nil; - if (amend) - self.amend = NO; - else - [self refresh]; - -} - -- (void)postCommitUpdate:(NSString *)update -{ - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexCommitStatus - object:self - userInfo:[NSDictionary dictionaryWithObject:update forKey:@"description"]]; -} - -- (void)postCommitFailure:(NSString *)reason -{ - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexCommitFailed - object:self - userInfo:[NSDictionary dictionaryWithObject:reason forKey:@"description"]]; -} - -- (void)postOperationFailed:(NSString *)description -{ - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexOperationFailed - object:self - userInfo:[NSDictionary dictionaryWithObject:description forKey:@"description"]]; -} - -- (BOOL)stageFiles:(NSArray *)stageFiles -{ - // Input string for update-index - // This will be a list of filenames that - // should be updated. It's similar to - // "git add -- - NSMutableString *input = [NSMutableString string]; - - for (PBChangedFile *file in stageFiles) { - [input appendFormat:@"%@\0", file.path]; - } - - int ret = 1; - [repository outputForArguments:[NSArray arrayWithObjects:@"update-index", @"--add", @"--remove", @"-z", @"--stdin", nil] - inputString:input - retValue:&ret]; - - if (ret) { - [self postOperationFailed:[NSString stringWithFormat:@"Error in staging files. Return value: %i", ret]]; - return NO; - } - - for (PBChangedFile *file in stageFiles) - { - file.hasUnstagedChanges = NO; - file.hasStagedChanges = YES; - } - - [self postIndexChange]; - return YES; -} - -// TODO: Refactor with above. What's a better name for this? -- (BOOL)unstageFiles:(NSArray *)unstageFiles -{ - NSMutableString *input = [NSMutableString string]; - - for (PBChangedFile *file in unstageFiles) { - [input appendString:[file indexInfo]]; - } - - int ret = 1; - [repository outputForArguments:[NSArray arrayWithObjects:@"update-index", @"-z", @"--index-info", nil] - inputString:input - retValue:&ret]; - - if (ret) - { - [self postOperationFailed:[NSString stringWithFormat:@"Error in unstaging files. Return value: %i", ret]]; - return NO; - } - - for (PBChangedFile *file in unstageFiles) - { - file.hasUnstagedChanges = YES; - file.hasStagedChanges = NO; - } - - [self postIndexChange]; - return YES; -} - -- (void)discardChangesForFiles:(NSArray *)discardFiles -{ - NSArray *paths = [discardFiles valueForKey:@"path"]; - NSString *input = [paths componentsJoinedByString:@"\0"]; - - NSArray *arguments = [NSArray arrayWithObjects:@"checkout-index", @"--index", @"--quiet", @"--force", @"-z", @"--stdin", nil]; - - int ret = 1; - [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:[workingDirectory path] inputString:input retValue:&ret]; - - if (ret) { - [self postOperationFailed:[NSString stringWithFormat:@"Discarding changes failed with return value %i", ret]]; - return; - } - - for (PBChangedFile *file in discardFiles) - file.hasUnstagedChanges = NO; - - [self postIndexChange]; -} - -- (BOOL)applyPatch:(NSString *)hunk stage:(BOOL)stage reverse:(BOOL)reverse; -{ - NSMutableArray *array = [NSMutableArray arrayWithObjects:@"apply", nil]; - if (stage) - [array addObject:@"--cached"]; - if (reverse) - [array addObject:@"--reverse"]; - - int ret = 1; - NSString *error = [repository outputForArguments:array - inputString:hunk - retValue:&ret]; - - if (ret) { - [self postOperationFailed:[NSString stringWithFormat:@"Applying patch failed with return value %i. Error: %@", ret, error]]; - return NO; - } - - // TODO: Try to be smarter about what to refresh - [self refresh]; - return YES; -} - - -- (NSString *)diffForFile:(PBChangedFile *)file staged:(BOOL)staged contextLines:(NSUInteger)context -{ - NSString *parameter = [NSString stringWithFormat:@"-U%u", context]; - if (staged) { - NSString *indexPath = [@":0:" stringByAppendingString:file.path]; - - if (file.status == NEW) - return [repository outputForArguments:[NSArray arrayWithObjects:@"show", indexPath, nil]]; - - return [repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-index", parameter, @"--cached", [self parentTree], @"--", file.path, nil]]; - } - - // unstaged - if (file.status == NEW) { - NSStringEncoding encoding; - NSError *error = nil; - NSString *path = [[repository workingDirectory] stringByAppendingPathComponent:file.path]; - NSString *contents = [NSString stringWithContentsOfFile:path - usedEncoding:&encoding - error:&error]; - if (error) - return nil; - - return contents; - } - - return [repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-files", parameter, @"--", file.path, nil]]; -} - -- (void)postIndexChange -{ - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexIndexUpdated - object:self]; -} - -# pragma mark WebKit Accessibility - -+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector -{ - return NO; -} - -@end - -@implementation PBGitIndex (IndexRefreshMethods) - -- (void)indexRefreshFinished:(NSNotification *)notification -{ - if ([(NSNumber *)[(NSDictionary *)[notification userInfo] objectForKey:@"NSFileHandleError"] intValue]) - { - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexIndexRefreshFailed - object:self - userInfo:[NSDictionary dictionaryWithObject:@"update-index failed" forKey:@"description"]]; - return; - } - - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexIndexRefreshStatus - object:self - userInfo:[NSDictionary dictionaryWithObject:@"update-index success" forKey:@"description"]]; - - // Now that the index is refreshed, we need to read the information from the index - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - - // Other files (not tracked, not ignored) - refreshStatus++; - NSFileHandle *handle = [PBEasyPipe handleForCommand:[PBGitBinary path] - withArgs:[NSArray arrayWithObjects:@"ls-files", @"--others", @"--exclude-standard", @"-z", nil] - inDir:[workingDirectory path]]; - [nc addObserver:self selector:@selector(readOtherFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - [handle readToEndOfFileInBackgroundAndNotify]; - - // Unstaged files - refreshStatus++; - handle = [PBEasyPipe handleForCommand:[PBGitBinary path] - withArgs:[NSArray arrayWithObjects:@"diff-files", @"-z", nil] - inDir:[workingDirectory path]]; - [nc addObserver:self selector:@selector(readUnstagedFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - [handle readToEndOfFileInBackgroundAndNotify]; - - // Staged files - refreshStatus++; - handle = [PBEasyPipe handleForCommand:[PBGitBinary path] - withArgs:[NSArray arrayWithObjects:@"diff-index", @"--cached", @"-z", [self parentTree], nil] - inDir:[workingDirectory path]]; - [nc addObserver:self selector:@selector(readStagedFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - [handle readToEndOfFileInBackgroundAndNotify]; -} - -- (void)readOtherFiles:(NSNotification *)notification -{ - NSArray *lines = [self linesFromNotification:notification]; - NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:[lines count]]; - // Other files are untracked, so we don't have any real index information. Instead, we can just fake it. - // The line below is not used at all, as for these files the commitBlob isn't set - NSArray *fileStatus = [NSArray arrayWithObjects:@":000000", @"100644", @"0000000000000000000000000000000000000000", @"0000000000000000000000000000000000000000", @"A", nil]; - for (NSString *path in lines) { - if ([path length] == 0) - continue; - [dictionary setObject:fileStatus forKey:path]; - } - - [self addFilesFromDictionary:dictionary staged:NO tracked:NO]; - [self indexStepComplete]; -} - -- (void) readStagedFiles:(NSNotification *)notification -{ - NSArray *lines = [self linesFromNotification:notification]; - NSMutableDictionary *dic = [self dictionaryForLines:lines]; - [self addFilesFromDictionary:dic staged:YES tracked:YES]; - [self indexStepComplete]; -} - -- (void) readUnstagedFiles:(NSNotification *)notification -{ - NSArray *lines = [self linesFromNotification:notification]; - NSMutableDictionary *dic = [self dictionaryForLines:lines]; - [self addFilesFromDictionary:dic staged:NO tracked:YES]; - [self indexStepComplete]; -} - -- (void) addFilesFromDictionary:(NSMutableDictionary *)dictionary staged:(BOOL)staged tracked:(BOOL)tracked -{ - // Iterate over all existing files - for (PBChangedFile *file in files) { - NSArray *fileStatus = [dictionary objectForKey:file.path]; - // Object found, this is still a cached / uncached thing - if (fileStatus) { - if (tracked) { - NSString *mode = [[fileStatus objectAtIndex:0] substringFromIndex:1]; - NSString *sha = [fileStatus objectAtIndex:2]; - file.commitBlobSHA = sha; - file.commitBlobMode = mode; - - if (staged) - file.hasStagedChanges = YES; - else - file.hasUnstagedChanges = YES; - if ([[fileStatus objectAtIndex:4] isEqualToString:@"D"]) - file.status = DELETED; - } else { - // Untracked file, set status to NEW, only unstaged changes - file.hasStagedChanges = NO; - file.hasUnstagedChanges = YES; - file.status = NEW; - } - - // We handled this file, remove it from the dictionary - [dictionary removeObjectForKey:file.path]; - } else { - // Object not found in the dictionary, so let's reset its appropriate - // change (stage or untracked) if necessary. - - // Staged dictionary, so file does not have staged changes - if (staged) - file.hasStagedChanges = NO; - // Tracked file does not have unstaged changes, file is not new, - // so we can set it to No. (If it would be new, it would not - // be in this dictionary, but in the "other dictionary"). - else if (tracked && file.status != NEW) - file.hasUnstagedChanges = NO; - // Unstaged, untracked dictionary ("Other" files), and file - // is indicated as new (which would be untracked), so let's - // remove it - else if (!tracked && file.status == NEW) - file.hasUnstagedChanges = NO; - } - } - - // Do new files only if necessary - if (![[dictionary allKeys] count]) - return; - - // All entries left in the dictionary haven't been accounted for - // above, so we need to add them to the "files" array - [self willChangeValueForKey:@"indexChanges"]; - for (NSString *path in [dictionary allKeys]) { - NSArray *fileStatus = [dictionary objectForKey:path]; - - PBChangedFile *file = [[PBChangedFile alloc] initWithPath:path]; - if ([[fileStatus objectAtIndex:4] isEqualToString:@"D"]) - file.status = DELETED; - else if([[fileStatus objectAtIndex:0] isEqualToString:@":000000"]) - file.status = NEW; - else - file.status = MODIFIED; - - if (tracked) { - file.commitBlobMode = [[fileStatus objectAtIndex:0] substringFromIndex:1]; - file.commitBlobSHA = [fileStatus objectAtIndex:2]; - } - - file.hasStagedChanges = staged; - file.hasUnstagedChanges = !staged; - - [files addObject:file]; - } - [self didChangeValueForKey:@"indexChanges"]; -} - -# pragma mark Utility methods -- (NSArray *)linesFromNotification:(NSNotification *)notification -{ - NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem]; - if (!data) - return [NSArray array]; - - NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - // FIXME: throw an error? - if (!string) - return [NSArray array]; - - // Strip trailing null - if ([string hasSuffix:@"\0"]) - string = [string substringToIndex:[string length]-1]; - - if ([string length] == 0) - return [NSArray array]; - - return [string componentsSeparatedByString:@"\0"]; -} - -- (NSMutableDictionary *)dictionaryForLines:(NSArray *)lines -{ - NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[lines count]/2]; - - // Fill the dictionary with the new information. These lines are in the form of: - // :00000 :0644 OTHER INDEX INFORMATION - // Filename - - NSAssert1([lines count] % 2 == 0, @"Lines must have an even number of lines: %@", lines); - - NSEnumerator *enumerator = [lines objectEnumerator]; - NSString *fileStatus; - while (fileStatus = [enumerator nextObject]) { - NSString *fileName = [enumerator nextObject]; - [dictionary setObject:[fileStatus componentsSeparatedByString:@" "] forKey:fileName]; - } - - return dictionary; -} - -// This method is called for each of the three processes from above. -// If all three are finished (self.busy == 0), then we can delete -// all files previously marked as deletable -- (void)indexStepComplete -{ - // if we're still busy, do nothing :) - if (--refreshStatus) { - [self postIndexChange]; - return; - } - - // At this point, all index operations have finished. - // We need to find all files that don't have either - // staged or unstaged files, and delete them - - NSMutableArray *deleteFiles = [NSMutableArray array]; - for (PBChangedFile *file in files) { - if (!file.hasStagedChanges && !file.hasUnstagedChanges) - [deleteFiles addObject:file]; - } - - if ([deleteFiles count]) { - [self willChangeValueForKey:@"indexChanges"]; - for (PBChangedFile *file in deleteFiles) - [files removeObject:file]; - [self didChangeValueForKey:@"indexChanges"]; - } - - [[NSNotificationCenter defaultCenter] postNotificationName:PBGitIndexFinishedIndexRefresh - object:self]; - [self postIndexChange]; - -} - -@end diff --git a/PBGitIndexController.h b/PBGitIndexController.h deleted file mode 100644 index 1a455e6d9..000000000 --- a/PBGitIndexController.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// PBGitIndexController.h -// GitX -// -// Created by Pieter de Bie on 18-11-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import -#import "PBGitCommitController.h" -#import "PBChangedFile.h" - -@interface PBGitIndexController : NSObject { - IBOutlet NSArrayController *stagedFilesController, *unstagedFilesController; - IBOutlet PBGitCommitController *commitController; - - IBOutlet NSTableView *unstagedTable; - IBOutlet NSTableView *stagedTable; -} - -- (IBAction) rowClicked:(NSCell *) sender; -- (IBAction) tableClicked:(NSTableView *)tableView; - -- (NSMenu *) menuForTable:(NSTableView *)table; -@end diff --git a/PBGitIndexController.m b/PBGitIndexController.m deleted file mode 100644 index 5d2a949e1..000000000 --- a/PBGitIndexController.m +++ /dev/null @@ -1,297 +0,0 @@ -// -// PBGitIndexController.m -// GitX -// -// Created by Pieter de Bie on 18-11-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import "PBGitIndexController.h" -#import "PBChangedFile.h" -#import "PBGitRepository.h" -#import "PBGitIndex.h" - -#define FileChangesTableViewType @"GitFileChangedType" - -@interface PBGitIndexController () -- (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force; -@end - -@implementation PBGitIndexController - -- (void)awakeFromNib -{ - [unstagedTable setDoubleAction:@selector(tableClicked:)]; - [stagedTable setDoubleAction:@selector(tableClicked:)]; - - [unstagedTable setTarget:self]; - [stagedTable setTarget:self]; - - [unstagedTable registerForDraggedTypes: [NSArray arrayWithObject:FileChangesTableViewType]]; - [stagedTable registerForDraggedTypes: [NSArray arrayWithObject:FileChangesTableViewType]]; -} - -// FIXME: Find a proper place for this method -- this is not it. -- (void)ignoreFiles:(NSArray *)files -{ - // Build output string - NSMutableArray *fileList = [NSMutableArray array]; - for (PBChangedFile *file in files) { - NSString *name = file.path; - if ([name length] > 0) - [fileList addObject:name]; - } - NSString *filesAsString = [fileList componentsJoinedByString:@"\n"]; - - // Write to the file - NSString *gitIgnoreName = [commitController.repository gitIgnoreFilename]; - - NSStringEncoding enc = NSUTF8StringEncoding; - NSError *error = nil; - NSMutableString *ignoreFile; - - if (![[NSFileManager defaultManager] fileExistsAtPath:gitIgnoreName]) { - ignoreFile = [filesAsString mutableCopy]; - } else { - ignoreFile = [NSMutableString stringWithContentsOfFile:gitIgnoreName usedEncoding:&enc error:&error]; - if (error) { - [[commitController.repository windowController] showErrorSheet:error]; - return; - } - // Add a newline if not yet present - if ([ignoreFile characterAtIndex:([ignoreFile length] - 1)] != '\n') - [ignoreFile appendString:@"\n"]; - [ignoreFile appendString:filesAsString]; - } - - [ignoreFile writeToFile:gitIgnoreName atomically:YES encoding:enc error:&error]; - if (error) - [[commitController.repository windowController] showErrorSheet:error]; -} - -# pragma mark Context Menu methods -- (BOOL) allSelectedCanBeIgnored:(NSArray *)selectedFiles -{ - for (PBChangedFile *selectedItem in selectedFiles) { - if (selectedItem.status != NEW) { - return NO; - } - } - return YES; -} - -- (NSMenu *) menuForTable:(NSTableView *)table -{ - NSMenu *menu = [[NSMenu alloc] init]; - id controller = [table tag] == 0 ? unstagedFilesController : stagedFilesController; - NSArray *selectedFiles = [controller selectedObjects]; - - // Unstaged changes - if ([table tag] == 0) { - NSMenuItem *stageItem = [[NSMenuItem alloc] initWithTitle:@"Stage Changes" action:@selector(stageFilesAction:) keyEquivalent:@""]; - [stageItem setTarget:self]; - [stageItem setRepresentedObject:selectedFiles]; - [menu addItem:stageItem]; - } - else if ([table tag] == 1) { - NSMenuItem *unstageItem = [[NSMenuItem alloc] initWithTitle:@"Unstage Changes" action:@selector(unstageFilesAction:) keyEquivalent:@""]; - [unstageItem setTarget:self]; - [unstageItem setRepresentedObject:selectedFiles]; - [menu addItem:unstageItem]; - } - - NSString *title = [selectedFiles count] == 1 ? @"Open file" : @"Open files"; - NSMenuItem *openItem = [[NSMenuItem alloc] initWithTitle:title action:@selector(openFilesAction:) keyEquivalent:@""]; - [openItem setTarget:self]; - [openItem setRepresentedObject:selectedFiles]; - [menu addItem:openItem]; - - // Attempt to ignore - if ([self allSelectedCanBeIgnored:selectedFiles]) { - NSString *ignoreText = [selectedFiles count] == 1 ? @"Ignore File": @"Ignore Files"; - NSMenuItem *ignoreItem = [[NSMenuItem alloc] initWithTitle:ignoreText action:@selector(ignoreFilesAction:) keyEquivalent:@""]; - [ignoreItem setTarget:self]; - [ignoreItem setRepresentedObject:selectedFiles]; - [menu addItem:ignoreItem]; - } - - if ([selectedFiles count] == 1) { - NSMenuItem *showInFinderItem = [[NSMenuItem alloc] initWithTitle:@"Show in Finder" action:@selector(showInFinderAction:) keyEquivalent:@""]; - [showInFinderItem setTarget:self]; - [showInFinderItem setRepresentedObject:selectedFiles]; - [menu addItem:showInFinderItem]; - } - - for (PBChangedFile *file in selectedFiles) - if (!file.hasUnstagedChanges) - return menu; - - NSMenuItem *discardItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes…" action:@selector(discardFilesAction:) keyEquivalent:@""]; - [discardItem setTarget:self]; - [discardItem setAlternate:NO]; - [discardItem setRepresentedObject:selectedFiles]; - - [menu addItem:discardItem]; - - NSMenuItem *discardForceItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes" action:@selector(forceDiscardFilesAction:) keyEquivalent:@""]; - [discardForceItem setTarget:self]; - [discardForceItem setAlternate:YES]; - [discardForceItem setRepresentedObject:selectedFiles]; - [discardForceItem setKeyEquivalentModifierMask:NSAlternateKeyMask]; - [menu addItem:discardForceItem]; - - return menu; -} - -- (void) stageFilesAction:(id) sender -{ - [commitController.index stageFiles:[sender representedObject]]; -} - -- (void) unstageFilesAction:(id) sender -{ - [commitController.index unstageFiles:[sender representedObject]]; -} - -- (void) openFilesAction:(id) sender -{ - NSArray *files = [sender representedObject]; - NSString *workingDirectory = [commitController.repository workingDirectory]; - for (PBChangedFile *file in files) - [[NSWorkspace sharedWorkspace] openFile:[workingDirectory stringByAppendingPathComponent:[file path]]]; -} - -- (void) ignoreFilesAction:(id) sender -{ - NSArray *selectedFiles = [sender representedObject]; - if ([selectedFiles count] == 0) - return; - - [self ignoreFiles:selectedFiles]; - [commitController.index refresh]; -} - -- (void)discardFilesAction:(id) sender -{ - NSArray *selectedFiles = [sender representedObject]; - if ([selectedFiles count] > 0) - [self discardChangesForFiles:selectedFiles force:FALSE]; -} - -- (void)forceDiscardFilesAction:(id) sender -{ - NSArray *selectedFiles = [sender representedObject]; - if ([selectedFiles count] > 0) - [self discardChangesForFiles:selectedFiles force:TRUE]; -} - -- (void) showInFinderAction:(id) sender -{ - NSArray *selectedFiles = [sender representedObject]; - if ([selectedFiles count] == 0) - return; - NSString *workingDirectory = [[commitController.repository workingDirectory] stringByAppendingString:@"/"]; - NSString *path = [workingDirectory stringByAppendingPathComponent:[[selectedFiles objectAtIndex:0] path]]; - NSWorkspace *ws = [NSWorkspace sharedWorkspace]; - [ws selectFile: path inFileViewerRootedAtPath:nil]; -} - -- (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force -{ - if (!force) { - int ret = [[NSAlert alertWithMessageText:@"Discard changes" - defaultButton:nil - alternateButton:@"Cancel" - otherButton:nil - informativeTextWithFormat:@"Are you sure you wish to discard the changes to this file?\n\nYou cannot undo this operation."] runModal]; - if (ret != NSAlertDefaultReturn) - return; - } - - [commitController.index discardChangesForFiles:files]; -} - -# pragma mark TableView icon delegate -- (void)tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)rowIndex -{ - id controller = [tableView tag] == 0 ? unstagedFilesController : stagedFilesController; - [[tableColumn dataCell] setImage:[[[controller arrangedObjects] objectAtIndex:rowIndex] icon]]; -} - -- (void) tableClicked:(NSTableView *) tableView -{ - NSArrayController *controller = [tableView tag] == 0 ? unstagedFilesController : stagedFilesController; - - NSIndexSet *selectionIndexes = [tableView selectedRowIndexes]; - NSArray *files = [[controller arrangedObjects] objectsAtIndexes:selectionIndexes]; - if ([tableView tag] == 0) - [commitController.index stageFiles:files]; - else - [commitController.index unstageFiles:files]; -} - -- (void) rowClicked:(NSCell *)sender -{ - NSTableView *tableView = (NSTableView *)[sender controlView]; - if([tableView numberOfSelectedRows] != 1) - return; - [self tableClicked: tableView]; -} - -- (BOOL)tableView:(NSTableView *)tv -writeRowsWithIndexes:(NSIndexSet *)rowIndexes - toPasteboard:(NSPasteboard*)pboard -{ - // Copy the row numbers to the pasteboard. - [pboard declareTypes:[NSArray arrayWithObjects:FileChangesTableViewType, NSFilenamesPboardType, nil] owner:self]; - - // Internal, for dragging from one tableview to the other - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; - [pboard setData:data forType:FileChangesTableViewType]; - - // External, to drag them to for example XCode or Textmate - NSArrayController *controller = [tv tag] == 0 ? unstagedFilesController : stagedFilesController; - NSArray *files = [[controller arrangedObjects] objectsAtIndexes:rowIndexes]; - NSString *workingDirectory = [commitController.repository workingDirectory]; - - NSMutableArray *filenames = [NSMutableArray arrayWithCapacity:[rowIndexes count]]; - for (PBChangedFile *file in files) - [filenames addObject:[workingDirectory stringByAppendingPathComponent:[file path]]]; - - [pboard setPropertyList:filenames forType:NSFilenamesPboardType]; - return YES; -} - -- (NSDragOperation)tableView:(NSTableView*)tableView - validateDrop:(id )info - proposedRow:(NSInteger)row - proposedDropOperation:(NSTableViewDropOperation)operation -{ - if ([info draggingSource] == tableView) - return NSDragOperationNone; - - [tableView setDropRow:-1 dropOperation:NSTableViewDropOn]; - return NSDragOperationCopy; -} - -- (BOOL)tableView:(NSTableView *)aTableView - acceptDrop:(id )info - row:(NSInteger)row - dropOperation:(NSTableViewDropOperation)operation -{ - NSPasteboard* pboard = [info draggingPasteboard]; - NSData* rowData = [pboard dataForType:FileChangesTableViewType]; - NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData]; - - NSArrayController *controller = [aTableView tag] == 0 ? stagedFilesController : unstagedFilesController; - NSArray *files = [[controller arrangedObjects] objectsAtIndexes:rowIndexes]; - - if ([aTableView tag] == 0) - [commitController.index unstageFiles:files]; - else - [commitController.index stageFiles:files]; - - return YES; -} - -@end diff --git a/PBGitLane.mm b/PBGitLane.mm deleted file mode 100644 index 2ae767f27..000000000 --- a/PBGitLane.mm +++ /dev/null @@ -1,40 +0,0 @@ -// -// PBGitLane.m -// GitX -// -// Created by Pieter de Bie on 27-08-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitLane.h" -//class PBGitLane { -// static int s_colorIndex; -// -// char d_sha[20]; -// int d_index; -// -//public: -// PBGitLane(NSString *sha); -// -// bool isCommit(NSString *sha) const; -// int index(); const; -// -// static resetColors(); -//}; - -int PBGitLane::s_colorIndex = 0; - -int PBGitLane::index() const -{ - return d_index; -} - -void PBGitLane::setSha(git_oid sha) -{ - d_sha = sha; -} - -void PBGitLane::resetColors() -{ - s_colorIndex = 0; -} diff --git a/PBGitRef.h b/PBGitRef.h deleted file mode 100644 index 578b56e91..000000000 --- a/PBGitRef.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// PBGitRef.h -// GitX -// -// Created by Pieter de Bie on 06-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import - - -@interface PBGitRef : NSObject { - NSString* ref; -} - -- (NSString*) shortName; -- (NSString*) type; -+ (PBGitRef*) refFromString: (NSString*) s; -- (PBGitRef*) initWithString: (NSString*) s; -@property(readonly) NSString* ref; - -@end diff --git a/PBGitRef.m b/PBGitRef.m deleted file mode 100644 index f345a0a7a..000000000 --- a/PBGitRef.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// PBGitRef.m -// GitX -// -// Created by Pieter de Bie on 06-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitRef.h" - - -@implementation PBGitRef - -@synthesize ref; -- (NSString*) shortName -{ - if ([self type]) - return [ref substringFromIndex:[[self type] length] + 7]; - return ref; -} - -- (NSString*) type -{ - if ([ref hasPrefix:@"refs/heads"]) - return @"head"; - if ([ref hasPrefix:@"refs/tags"]) - return @"tag"; - if ([ref hasPrefix:@"refs/remotes"]) - return @"remote"; - return nil; -} - -+ (PBGitRef*) refFromString: (NSString*) s -{ - return [[PBGitRef alloc] initWithString:s]; -} - -- (PBGitRef*) initWithString: (NSString*) s -{ - ref = s; - return self; -} - -+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector -{ - return NO; -} - -+ (BOOL)isKeyExcludedFromWebScript:(const char *)name { - return NO; -} - -@end diff --git a/PBGitRepository.h b/PBGitRepository.h deleted file mode 100644 index 93c38c9fa..000000000 --- a/PBGitRepository.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// PBGitRepository.h -// GitTest -// -// Created by Pieter de Bie on 13-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import "PBGitRevList.h" -#import "PBGitRevSpecifier.h" -#import "PBGitConfig.h" - -extern NSString* PBGitRepositoryErrorDomain; - -@class PBGitWindowController; - -@interface PBGitRepository : NSDocument { - PBGitRevList* revisionList; - PBGitConfig *config; - - BOOL hasChanged; - NSMutableArray *branches; - PBGitRevSpecifier *currentBranch; - NSMutableDictionary *refs; - - PBGitRevSpecifier *_headRef; // Caching -} - -- (NSFileHandle*) handleForCommand:(NSString*) cmd; -- (NSFileHandle*) handleForArguments:(NSArray*) args; -- (NSFileHandle *) handleInWorkDirForArguments:(NSArray *)args; -- (NSString*) outputForCommand:(NSString*) cmd; -- (NSString *)outputForCommand:(NSString *)str retValue:(int *)ret; -- (NSString *)outputForArguments:(NSArray *)arguments inputString:(NSString *)input retValue:(int *)ret; -- (NSString *)outputForArguments:(NSArray *)arguments inputString:(NSString *)input byExtendingEnvironment:(NSDictionary *)dict retValue:(int *)ret; - - -- (NSString*) outputForArguments:(NSArray*) args; -- (NSString*) outputForArguments:(NSArray*) args retValue:(int *)ret; -- (NSString *)outputInWorkdirForArguments:(NSArray*) arguments; -- (NSString *)outputInWorkdirForArguments:(NSArray*) arguments retValue:(int *)ret; -- (BOOL)executeHook:(NSString *)name output:(NSString **)output; -- (BOOL)executeHook:(NSString *)name withArgs:(NSArray*) arguments output:(NSString **)output; - -- (NSString *)workingDirectory; -- (NSString *)gitIgnoreFilename; -- (BOOL)isBareRepository; - -- (BOOL) reloadRefs; -- (void) addRef:(PBGitRef *)ref fromParameters:(NSArray *)params; -- (void) lazyReload; -- (PBGitRevSpecifier*) headRef; - -- (void) readCurrentBranch; -- (PBGitRevSpecifier*) addBranch: (PBGitRevSpecifier*) rev; -- (BOOL)removeBranch:(PBGitRevSpecifier *)rev; - -- (NSString*) parseSymbolicReference:(NSString*) ref; -- (NSString*) parseReference:(NSString*) ref; - -+ (NSURL*)gitDirForURL:(NSURL*)repositoryURL; -+ (NSURL*)baseDirForURL:(NSURL*)repositoryURL; - -- (id) initWithURL: (NSURL*) path; -- (void) setup; - -@property (assign) BOOL hasChanged; -@property (readonly) PBGitWindowController *windowController; -@property (readonly) PBGitConfig *config; -@property (retain) PBGitRevList* revisionList; -@property (assign) NSMutableArray* branches; -@property (assign) PBGitRevSpecifier *currentBranch; -@property (retain) NSMutableDictionary* refs; -@end diff --git a/PBGitRepository.m b/PBGitRepository.m deleted file mode 100644 index 821cd2a5b..000000000 --- a/PBGitRepository.m +++ /dev/null @@ -1,460 +0,0 @@ -// -// 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 "PBGitBinary.h" - -#import "NSFileHandleExt.h" -#import "PBEasyPipe.h" -#import "PBGitRef.h" -#import "PBGitRevSpecifier.h" - -NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain"; - -@implementation PBGitRepository - -@synthesize revisionList, branches, currentBranch, refs, hasChanged, config; - -- (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:[PBGitBinary path] withArgs:[NSArray arrayWithObjects:@"rev-parse", @"--is-bare-repository", nil] inDir:path] isEqualToString:@"true"]; -} - -+ (NSURL*)gitDirForURL:(NSURL*)repositoryURL; -{ - if (![PBGitBinary path]) - return nil; - - 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:[PBGitBinary path] 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; -} - -// NSFileWrapper is broken and doesn't work when called on a directory containing a large number of directories and files. -//because of this it is safer to implement readFromURL than readFromFileWrapper. -//Because NSFileManager does not attempt to recursively open all directories and file when fileExistsAtPath is called -//this works much better. -- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError -{ - if (![PBGitBinary path]) - { - if (outError) { - NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[PBGitBinary notFoundError] - forKey:NSLocalizedRecoverySuggestionErrorKey]; - *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo]; - } - return NO; - } - - BOOL isDirectory = FALSE; - [[NSFileManager defaultManager] fileExistsAtPath:[absoluteURL path] isDirectory:&isDirectory]; - if (!isDirectory) { - if (outError) { - NSDictionary* userInfo = [NSDictionary dictionaryWithObject:@"Reading files is not supported." - forKey:NSLocalizedRecoverySuggestionErrorKey]; - *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo]; - } - return NO; - } - - - NSURL* gitDirURL = [PBGitRepository gitDirForURL:[self fileURL]]; - if (!gitDirURL) { - if (outError) { - NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ does not appear to be a git repository.", [self fileName]] - forKey:NSLocalizedRecoverySuggestionErrorKey]; - *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo]; - } - return NO; - } - - [self setFileURL:gitDirURL]; - [self setup]; - [self readCurrentBranch]; - return YES; -} - -- (void) setup -{ - config = [[PBGitConfig alloc] initWithRepository:self.fileURL.path]; - self.branches = [NSMutableArray array]; - [self reloadRefs]; - revisionList = [[PBGitRevList alloc] initWithRepository:self]; -} - -- (id) initWithURL: (NSURL*) path -{ - if (![PBGitBinary path]) - return nil; - - NSURL* gitDirURL = [PBGitRepository gitDirForURL:path]; - if (!gitDirURL) - return nil; - - self = [self init]; - [self setFileURL: gitDirURL]; - - [self setup]; - - // We don't want the window controller to display anything yet.. - // We'll leave that to the caller of this method. -#ifndef CLI - [self addWindowController:[[PBGitWindowController alloc] initWithRepository:self displayDefault:NO]]; -#endif - - [self showWindows]; - - return self; -} - -// 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* dirName = self.fileURL.path.lastPathComponent; - if ([dirName isEqualToString:@".git"]) - dirName = [self.fileURL.path stringByDeletingLastPathComponent].lastPathComponent; - NSString* displayName; - if (![[PBGitRef refFromString:[[self headRef] simpleRef]] type]) { - displayName = [NSString stringWithFormat:@"%@ (detached HEAD)", dirName]; - } else { - displayName = [NSString stringWithFormat:@"%@ (branch: %@)", dirName, - [[self headRef] description]]; - } - - return displayName; -} - -// Get the .gitignore file at the root of the repository -- (NSString*)gitIgnoreFilename -{ - return [[self workingDirectory] stringByAppendingPathComponent:@".gitignore"]; -} - -- (BOOL)isBareRepository -{ - if([self workingDirectory]) { - return [PBGitRepository isBareRepository:[self workingDirectory]]; - } else { - return true; - } -} - -// Overridden to create our custom window controller -- (void)makeWindowControllers -{ -#ifndef CLI - [self addWindowController: [[PBGitWindowController alloc] initWithRepository:self displayDefault:YES]]; -#endif -} - -- (PBGitWindowController *)windowController -{ - if ([[self windowControllers] count] == 0) - return NULL; - - return [[self windowControllers] objectAtIndex:0]; -} - -- (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 -{ - _headRef = nil; - BOOL ret = NO; - - refs = [NSMutableDictionary dictionary]; - - NSString* output = [PBEasyPipe outputForCommand:[PBGitBinary path] - withArgs:[NSArray arrayWithObjects:@"for-each-ref", @"--format=%(refname) %(objecttype) %(objectname)" - " %(*objectname)", @"refs", nil] - inDir: self.fileURL.path]; - NSArray* lines = [output componentsSeparatedByString:@"\n"]; - - for (NSString* line in lines) { - // If its an empty line, skip it (e.g. with empty repositories) - if ([line length] == 0) - continue; - - 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 ([self addBranch:revSpec] != revSpec) - ret = YES; - - // Also add this ref to the refs list - [self addRef:newRef fromParameters:components]; - } - - // Add an "All branches" option in the branches list - [self addBranch:[PBGitRevSpecifier allBranchesRevSpec]]; - [self addBranch:[PBGitRevSpecifier localBranchesRevSpec]]; - - [[[self windowController] window] setTitle:[self displayName]]; - - return ret; -} - -- (void) lazyReload -{ - if (!hasChanged) - return; - - [self reloadRefs]; - [self.revisionList reload]; - hasChanged = NO; -} - -- (PBGitRevSpecifier *)headRef -{ - if (_headRef) - return _headRef; - - NSString* branch = [self parseSymbolicReference: @"HEAD"]; - if (branch && [branch hasPrefix:@"refs/heads/"]) - _headRef = [[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:branch]]; - else - _headRef = [[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:@"HEAD"]]; - - return _headRef; -} - -// 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; -} - -- (BOOL)removeBranch:(PBGitRevSpecifier *)rev -{ - for (PBGitRevSpecifier *r in branches) { - if ([rev isEqualTo:r]) { - [self willChangeValueForKey:@"branches"]; - [branches removeObject:r]; - [self didChangeValueForKey:@"branches"]; - return TRUE; - } - } - return FALSE; -} - -- (void) readCurrentBranch -{ - self.currentBranch = [self addBranch: [self headRef]]; -} - -- (NSString *) workingDirectory -{ - if ([self.fileURL.path hasSuffix:@"/.git"]) - return [self.fileURL.path substringToIndex:[self.fileURL.path length] - 5]; - else if ([[self outputForCommand:@"rev-parse --is-inside-work-tree"] isEqualToString:@"true"]) - return [PBGitBinary path]; - - return nil; -} - -- (int) returnValueForCommand:(NSString *)cmd -{ - int i; - [self outputForCommand:cmd retValue: &i]; - return i; -} - -- (NSFileHandle*) handleForArguments:(NSArray *)args -{ - NSString* gitDirArg = [@"--git-dir=" stringByAppendingString:self.fileURL.path]; - NSMutableArray* arguments = [NSMutableArray arrayWithObject: gitDirArg]; - [arguments addObjectsFromArray: args]; - return [PBEasyPipe handleForCommand:[PBGitBinary path] withArgs:arguments]; -} - -- (NSFileHandle*) handleInWorkDirForArguments:(NSArray *)args -{ - NSString* gitDirArg = [@"--git-dir=" stringByAppendingString:self.fileURL.path]; - NSMutableArray* arguments = [NSMutableArray arrayWithObject: gitDirArg]; - [arguments addObjectsFromArray: args]; - return [PBEasyPipe handleForCommand:[PBGitBinary path] withArgs:arguments inDir:[self workingDirectory]]; -} - -- (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:[PBGitBinary path] withArgs:arguments inDir: self.fileURL.path]; -} - -- (NSString*) outputInWorkdirForArguments:(NSArray*) arguments -{ - return [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir: [self workingDirectory]]; -} - -- (NSString*) outputInWorkdirForArguments:(NSArray *)arguments retValue:(int *)ret -{ - return [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:[self workingDirectory] retValue: ret]; -} - -- (NSString*) outputForArguments:(NSArray *)arguments retValue:(int *)ret -{ - return [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir: self.fileURL.path retValue: ret]; -} - -- (NSString*) outputForArguments:(NSArray *)arguments inputString:(NSString *)input retValue:(int *)ret -{ - return [PBEasyPipe outputForCommand:[PBGitBinary path] - withArgs:arguments - inDir:[self workingDirectory] - inputString:input - retValue: ret]; -} - -- (NSString *)outputForArguments:(NSArray *)arguments inputString:(NSString *)input byExtendingEnvironment:(NSDictionary *)dict retValue:(int *)ret -{ - return [PBEasyPipe outputForCommand:[PBGitBinary path] - withArgs:arguments - inDir:[self workingDirectory] - byExtendingEnvironment:dict - inputString:input - retValue: ret]; -} - -- (BOOL)executeHook:(NSString *)name output:(NSString **)output -{ - return [self executeHook:name withArgs:[NSArray array] output:output]; -} - -- (BOOL)executeHook:(NSString *)name withArgs:(NSArray *)arguments output:(NSString **)output -{ - NSString *hookPath = [[[[self fileURL] path] stringByAppendingPathComponent:@"hooks"] stringByAppendingPathComponent:name]; - if (![[NSFileManager defaultManager] isExecutableFileAtPath:hookPath]) - return TRUE; - - NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys: - [self fileURL].path, @"GIT_DIR", - [[self fileURL].path stringByAppendingPathComponent:@"index"], @"GIT_INDEX_FILE", - nil - ]; - - int ret = 1; - NSString *_output = [PBEasyPipe outputForCommand:hookPath withArgs:arguments inDir:[self workingDirectory] byExtendingEnvironment:info inputString:nil retValue:&ret]; - - if (output) - *output = _output; - - return ret == 0; -} - -- (NSString *)parseReference:(NSString *)reference -{ - int ret = 1; - NSString *ref = [self outputForArguments:[NSArray arrayWithObjects: @"rev-parse", @"--verify", reference, nil] retValue: &ret]; - if (ret) - return nil; - - return ref; -} - -- (NSString*) parseSymbolicReference:(NSString*) reference -{ - NSString* ref = [self outputForArguments:[NSArray arrayWithObjects: @"symbolic-ref", @"-q", reference, nil]]; - if ([ref hasPrefix:@"refs/"]) - return ref; - - return nil; -} - -- (void) finalize -{ - NSLog(@"Dealloc of repository"); - [super finalize]; -} -@end diff --git a/PBGitRevList.h b/PBGitRevList.h deleted file mode 100644 index 42bc82af3..000000000 --- a/PBGitRevList.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// PBGitRevList.h -// GitX -// -// Created by Pieter de Bie on 17-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import - -@class PBGitRepository; - -@interface PBGitRevList : NSObject { - NSArray* commits; - PBGitRepository *repository; - NSString* lastSha; -} - -- initWithRepository:(PBGitRepository *)repo; -- (void) readCommitsForce: (BOOL) force; -- (void) reload; - -@property(retain) NSArray* commits; - -@end diff --git a/PBGitRevList.mm b/PBGitRevList.mm deleted file mode 100644 index ca3704f31..000000000 --- a/PBGitRevList.mm +++ /dev/null @@ -1,201 +0,0 @@ -// -// PBGitRevList.m -// GitX -// -// Created by Pieter de Bie on 17-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitRevList.h" -#import "PBGitRepository.h" -#import "PBGitCommit.h" -#import "PBGitGrapher.h" -#import "PBGitRevSpecifier.h" - -#include "git/oid.h" -#include -#include -#include -#include - -using namespace std; - -@implementation PBGitRevList - -@synthesize commits; -- (id)initWithRepository:(PBGitRepository *)repo -{ - repository = repo; - [repository addObserver:self forKeyPath:@"currentBranch" options:0 context:nil]; - - return self; -} - -- (void) reload -{ - [self readCommitsForce: YES]; -} - -- (void) readCommitsForce: (BOOL) force -{ - // We use refparse to get the commit sha that we will parse. That way, - // we can check if the current branch is the same as the previous one - // and in that case we don't have to reload the revision list. - - // If no branch is selected, don't do anything - if (![repository currentBranch]) - return; - - PBGitRevSpecifier* newRev = [repository currentBranch]; - NSString* newSha = nil; - - if (!force && newRev && [newRev isSimpleRef]) { - newSha = [repository parseReference:[newRev simpleRef]]; - if ([newSha isEqualToString:lastSha]) - return; - } - lastSha = newSha; - - NSThread * commitThread = [[NSThread alloc] initWithTarget: self selector: @selector(walkRevisionListWithSpecifier:) object:newRev]; - [commitThread start]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object - change:(NSDictionary *)change context:(void *)context -{ - if (object == repository) - [self readCommitsForce: NO]; -} - -- (void) walkRevisionListWithSpecifier: (PBGitRevSpecifier*) rev -{ - NSDate *start = [NSDate date]; - NSMutableArray* revisions = [NSMutableArray array]; - PBGitGrapher* g = [[PBGitGrapher alloc] initWithRepository: repository]; - std::map encodingMap; - - NSString *formatString = @"--pretty=format:%H\01%e\01%an\01%s\01%P\01%at"; - BOOL showSign = [rev hasLeftRight]; - - if (showSign) - formatString = [formatString stringByAppendingString:@"\01%m"]; - - NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"log", @"-z", @"--early-output", @"--topo-order", @"--children", formatString, nil]; - - if (!rev) - [arguments addObject:@"HEAD"]; - else - [arguments addObjectsFromArray:[rev parameters]]; - - NSString *directory = rev.workingDirectory ? rev.workingDirectory.path : repository.fileURL.path; - NSTask *task = [PBEasyPipe taskForCommand:[PBGitBinary path] withArgs:arguments inDir:directory]; - [task launch]; - NSFileHandle* handle = [task.standardOutput fileHandleForReading]; - - int fd = [handle fileDescriptor]; - __gnu_cxx::stdio_filebuf buf(fd, std::ios::in); - std::istream stream(&buf); - - int num = 0; - while (true) { - string sha; - if (!getline(stream, sha, '\1')) - break; - - // We reached the end of some temporary output. Show what we have - // until now, and then start again. The sha of the next thing is still - // in this buffer. So, we use a substring of current input. - if (sha[1] == 'i') // Matches 'Final output' - { - num = 0; - [self performSelectorOnMainThread:@selector(setCommits:) withObject:revisions waitUntilDone:NO]; - g = [[PBGitGrapher alloc] initWithRepository: repository]; - revisions = [NSMutableArray array]; - - // If the length is < 40, then there are no commits.. quit now - if (sha.length() < 40) - break; - - sha = sha.substr(sha.length() - 40, 40); - } - - // From now on, 1.2 seconds - string encoding_str; - getline(stream, encoding_str, '\1'); - NSStringEncoding encoding = NSUTF8StringEncoding; - if (encoding_str.length()) - { - if (encodingMap.find(encoding_str) != encodingMap.end()) { - encoding = encodingMap[encoding_str]; - } else { - encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)[NSString stringWithUTF8String:encoding_str.c_str()])); - encodingMap[encoding_str] = encoding; - } - } - - git_oid oid; - git_oid_mkstr(&oid, sha.c_str()); - PBGitCommit* newCommit = [[PBGitCommit alloc] initWithRepository:repository andSha:oid]; - - string author; - getline(stream, author, '\1'); - - string subject; - getline(stream, subject, '\1'); - - string parentString; - getline(stream, parentString, '\1'); - if (parentString.size() != 0) - { - if (((parentString.size() + 1) % 41) != 0) { - NSLog(@"invalid parents: %i", parentString.size()); - continue; - } - int nParents = (parentString.size() + 1) / 41; - git_oid *parents = (git_oid *)malloc(sizeof(git_oid) * nParents); - int parentIndex; - for (parentIndex = 0; parentIndex < nParents; ++parentIndex) - git_oid_mkstr(parents + parentIndex, parentString.substr(parentIndex * 41, 40).c_str()); - - newCommit.parentShas = parents; - newCommit.nParents = nParents; - } - - int time; - stream >> time; - - - [newCommit setSubject:[NSString stringWithCString:subject.c_str() encoding:encoding]]; - [newCommit setAuthor:[NSString stringWithCString:author.c_str() encoding:encoding]]; - [newCommit setTimestamp:time]; - - if (showSign) - { - char c; - stream >> c; // Remove separator - stream >> c; - if (c != '>' && c != '<' && c != '^' && c != '-') - NSLog(@"Error loading commits: sign not correct"); - [newCommit setSign: c]; - } - - char c; - stream >> c; - if (c != '\0') - cout << "Error" << endl; - - [revisions addObject: newCommit]; - [g decorateCommit: newCommit]; - - if (++num % 1000 == 0) - [self performSelectorOnMainThread:@selector(setCommits:) withObject:revisions waitUntilDone:NO]; - } - - NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start]; - NSLog(@"Loaded %i commits in %f seconds", num, duration); - // Make sure the commits are stored before exiting. - [self performSelectorOnMainThread:@selector(setCommits:) withObject:revisions waitUntilDone:YES]; - [task waitUntilExit]; -} - -@end diff --git a/PBGitRevSpecifier.m b/PBGitRevSpecifier.m deleted file mode 100644 index 0fe90937f..000000000 --- a/PBGitRevSpecifier.m +++ /dev/null @@ -1,103 +0,0 @@ -// -// PBGitRevSpecifier.m -// GitX -// -// Created by Pieter de Bie on 12-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitRevSpecifier.h" - - -@implementation PBGitRevSpecifier - -@synthesize parameters, description, workingDirectory; - -- (id) initWithParameters:(NSArray*) params -{ - parameters = params; - description = nil; - return self; -} - -- (id) initWithRef: (PBGitRef*) ref -{ - parameters = [NSArray arrayWithObject: ref.ref]; - description = ref.shortName; - return self; -} - -- (id) initWithCoder:(NSCoder *)coder -{ - parameters = [coder decodeObjectForKey:@"Parameters"]; - description = [coder decodeObjectForKey:@"Description"]; - return self; -} - -+ (PBGitRevSpecifier *)allBranchesRevSpec -{ - id revspec = [[PBGitRevSpecifier alloc] initWithParameters:[NSArray arrayWithObject:@"--all"]]; - [revspec setDescription:@"All branches"]; - return revspec; -} - -+ (PBGitRevSpecifier *)localBranchesRevSpec -{ - id revspec = [[PBGitRevSpecifier alloc] initWithParameters:[NSArray arrayWithObject:@"--branches"]]; - [revspec setDescription:@"Local branches"]; - return revspec; -} -- (BOOL) isSimpleRef -{ - return ([parameters count] == 1 && ![[parameters objectAtIndex:0] hasPrefix:@"-"]); -} - -- (NSString*) simpleRef -{ - if (![self isSimpleRef]) - return nil; - return [parameters objectAtIndex:0]; -} - -- (NSString*) description -{ - if (description) - return description; - - return [parameters componentsJoinedByString:@" "]; -} - -- (BOOL) hasPathLimiter; -{ - for (NSString* param in parameters) - if ([param isEqualToString:@"--"]) - return YES; - return NO; -} - -- (BOOL) hasLeftRight -{ - for (NSString* param in parameters) - if ([param isEqualToString:@"--left-right"]) - return YES; - return NO; -} - -- (BOOL) isEqualTo: (PBGitRevSpecifier*) other -{ - if ([self isSimpleRef] ^ [other isSimpleRef]) - return NO; - - if ([self isSimpleRef]) - return [[[self parameters] objectAtIndex: 0] isEqualToString: [other.parameters objectAtIndex: 0]]; - - return ([[parameters componentsJoinedByString:@" "] isEqualToString: [other.parameters componentsJoinedByString:@" "]] && - (!description || [description isEqualToString:other.description])); -} - -- (void) encodeWithCoder:(NSCoder *)coder -{ - [coder encodeObject:description forKey:@"Description"]; - [coder encodeObject:parameters forKey:@"Parameters"]; -} -@end diff --git a/PBGitRevisionCell.m b/PBGitRevisionCell.m deleted file mode 100644 index 98f978bbb..000000000 --- a/PBGitRevisionCell.m +++ /dev/null @@ -1,302 +0,0 @@ -// -// PBGitRevisionCell.m -// GitX -// -// Created by Pieter de Bie on 17-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitRevisionCell.h" -#import "PBGitRef.h" -#import "RoundedRectangle.h" - -@implementation PBGitRevisionCell - - -- (id) initWithCoder: (id) coder -{ - self = [super initWithCoder:coder]; - textCell = [[NSTextFieldCell alloc] initWithCoder:coder]; - return self; -} - -- (NSArray*) colors -{ - return [NSArray arrayWithObjects: - [NSColor colorWithCalibratedRed: 0X4e/256.0 green:0X9A/256.0 blue: 0X06/256.0 alpha: 1.0], - [NSColor colorWithCalibratedRed: 0X20/256.0 green:0X4A/256.0 blue: 0X87/256.0 alpha: 1.0], - [NSColor colorWithCalibratedRed: 0XC4/256.0 green:0XA0/256.0 blue: 0 alpha: 1.0], - [NSColor colorWithCalibratedRed: 0X5C/256.0 green:0X35/256.0 blue: 0X66/256.0 alpha: 1.0], - [NSColor colorWithCalibratedRed: 0XA4/256.0 green:0X00/256.0 blue: 0X00/256.0 alpha: 1.0], - [NSColor colorWithCalibratedRed: 0XCE/256.0 green:0X5C/256.0 blue: 0 alpha: 1.0], - nil]; -} - -- (void) drawLineFromColumn: (int) from toColumn: (int) to inRect: (NSRect) r offset: (int) offset color: (int) c -{ - - int columnWidth = 10; - NSPoint origin = r.origin; - - NSPoint source = NSMakePoint(origin.x + columnWidth* from, origin.y + offset); - NSPoint center = NSMakePoint( origin.x + columnWidth * to, origin.y + r.size.height * 0.5 + 0.5); - - // Just use red for now. - NSArray* colors = [self colors]; - [[colors objectAtIndex: c % [colors count]] set]; - - NSBezierPath * path = [NSBezierPath bezierPath]; - [path setLineWidth:2]; - - [path moveToPoint: source]; - [path lineToPoint: center]; - [path stroke]; - -} - -- (void) drawCircleInRect: (NSRect) r -{ - - int c = cellInfo.position; - int columnWidth = 10; - NSPoint origin = r.origin; - NSPoint columnOrigin = { origin.x + columnWidth * c, origin.y}; - - NSRect oval = { columnOrigin.x - 5, columnOrigin.y + r.size.height * 0.5 - 5, 10, 10}; - - - NSBezierPath * path = [NSBezierPath bezierPathWithOvalInRect:oval]; - - [[NSColor blackColor] set]; - [path fill]; - - NSRect smallOval = { columnOrigin.x - 3, columnOrigin.y + r.size.height * 0.5 - 3, 6, 6}; - [[NSColor whiteColor] set]; - path = [NSBezierPath bezierPathWithOvalInRect:smallOval]; - [path fill]; -} - -- (void) drawTriangleInRect: (NSRect) r sign: (char) sign -{ - int c = cellInfo.position; - int columnHeight = 10; - int columnWidth = 8; - - NSPoint top; - if (sign == '<') - top.x = round(r.origin.x) + 10 * c + 4; - else { - top.x = round(r.origin.x) + 10 * c - 4; - columnWidth *= -1; - } - top.y = r.origin.y + (r.size.height - columnHeight) / 2; - - NSBezierPath * path = [NSBezierPath bezierPath]; - // Start at top - [path moveToPoint: NSMakePoint(top.x, top.y)]; - // Go down - [path lineToPoint: NSMakePoint(top.x, top.y + columnHeight)]; - // Go left top - [path lineToPoint: NSMakePoint(top.x - columnWidth, top.y + columnHeight / 2)]; - // Go to top again - [path closePath]; - - [[NSColor whiteColor] set]; - [path fill]; - [[NSColor blackColor] set]; - [path setLineWidth: 2]; - [path stroke]; -} - -- (NSMutableDictionary*) attributesForRefLabelSelected: (BOOL) selected -{ - NSMutableDictionary *attributes = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease]; - NSMutableParagraphStyle* style = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease]; - - [style setAlignment:NSCenterTextAlignment]; - [attributes setObject:style forKey:NSParagraphStyleAttributeName]; - [attributes setObject:[NSFont fontWithName:@"Helvetica" size:9] forKey:NSFontAttributeName]; - - //if (selected) - // [attributes setObject:[NSColor alternateSelectedControlTextColor] forKey:NSForegroundColorAttributeName]; - - return attributes; -} - -- (NSColor*) colorForRef: (PBGitRef*) ref -{ - BOOL isHEAD = [ref.ref isEqualToString:[[[controller repository] headRef] simpleRef]]; - - if (isHEAD) - return [NSColor colorWithCalibratedRed: 0Xfc/256.0 green:0Xa6/256.0 blue: 0X4f/256.0 alpha: 1.0]; - - NSString* type = [ref type]; - if ([type isEqualToString:@"head"]) - return [NSColor colorWithCalibratedRed: 0Xaa/256.0 green:0Xf2/256.0 blue: 0X54/256.0 alpha: 1.0]; - else if ([type isEqualToString:@"remote"]) - return [NSColor colorWithCalibratedRed: 0xb2/256.0 green:0Xdf/256.0 blue: 0Xff/256.0 alpha: 1.0]; - else if ([type isEqualToString:@"tag"]) - return [NSColor colorWithCalibratedRed: 0Xfc/256.0 green:0Xed/256.0 blue: 0X4f/256.0 alpha: 1.0]; - - return [NSColor yellowColor]; -} - --(NSArray *)rectsForRefsinRect:(NSRect) rect; -{ - NSMutableArray *array = [NSMutableArray array]; - - static const int ref_padding = 10; - static const int ref_spacing = 2; - - NSRect lastRect = rect; - lastRect.origin.x = round(lastRect.origin.x) - 0.5; - lastRect.origin.y = round(lastRect.origin.y) - 0.5; - - for (PBGitRef *ref in self.objectValue.refs) { - NSMutableDictionary* attributes = [self attributesForRefLabelSelected:NO]; - NSSize textSize = [[ref shortName] sizeWithAttributes:attributes]; - - NSRect newRect = lastRect; - newRect.size.width = textSize.width + ref_padding; - newRect.size.height = textSize.height; - newRect.origin.y = rect.origin.y + (rect.size.height - newRect.size.height) / 2; - - [array addObject:[NSValue valueWithRect:newRect]]; - lastRect = newRect; - lastRect.origin.x += (int)lastRect.size.width + ref_spacing; - } - - return array; -} - -- (void) drawLabelAtIndex:(int)index inRect:(NSRect)rect -{ - NSArray *refs = self.objectValue.refs; - PBGitRef *ref = [refs objectAtIndex:index]; - - NSMutableDictionary* attributes = [self attributesForRefLabelSelected:[self isHighlighted]]; - NSBezierPath *border = [NSBezierPath bezierPathWithRoundedRect:rect cornerRadius: 2.0]; - [[self colorForRef:ref] set]; - [border fill]; - - [[ref shortName] drawInRect:rect withAttributes:attributes]; - [border stroke]; -} - -- (void) drawRefsInRect: (NSRect *)refRect -{ - [[NSColor blackColor] setStroke]; - - NSRect lastRect; - int index = 0; - for (NSValue *rectValue in [self rectsForRefsinRect:*refRect]) - { - NSRect rect = [rectValue rectValue]; - [self drawLabelAtIndex:index inRect:rect]; - lastRect = rect; - ++index; - } - refRect->size.width -= lastRect.origin.x - refRect->origin.x + lastRect.size.width; - refRect->origin.x = lastRect.origin.x + lastRect.size.width; -} - -- (void) drawWithFrame: (NSRect) rect inView:(NSView *)view -{ - cellInfo = [self.objectValue lineInfo]; - - if (cellInfo && ![controller hasNonlinearPath]) { - float pathWidth = 10 + 10 * cellInfo.numColumns; - - NSRect ownRect; - NSDivideRect(rect, &ownRect, &rect, pathWidth, NSMinXEdge); - - int i; - struct PBGitGraphLine *lines = cellInfo.lines; - for (i = 0; i < cellInfo.nLines; i++) { - if (lines[i].upper == 0) - [self drawLineFromColumn: lines[i].from toColumn: lines[i].to inRect:ownRect offset: ownRect.size.height color: lines[i].colorIndex]; - else - [self drawLineFromColumn: lines[i].from toColumn: lines[i].to inRect:ownRect offset: 0 color:lines[i].colorIndex]; - } - - if (cellInfo.sign == '<' || cellInfo.sign == '>') - [self drawTriangleInRect: ownRect sign: cellInfo.sign]; - else - [self drawCircleInRect: ownRect]; - } - - - if ([self.objectValue refs] && [[self.objectValue refs] count]) - [self drawRefsInRect:&rect]; - - // Still use this superclass because of hilighting differences - //_contents = [self.objectValue subject]; - //[super drawWithFrame:rect inView:view]; - [textCell setObjectValue: [self.objectValue subject]]; - [textCell setHighlighted: [self isHighlighted]]; - [textCell drawWithFrame:rect inView: view]; -} - -- (void) setObjectValue: (PBGitCommit*)object { - [super setObjectValue:[NSValue valueWithNonretainedObject:object]]; -} - -- (PBGitCommit*) objectValue { - return [[super objectValue] nonretainedObjectValue]; -} - -- (int) indexAtX:(float)x -{ - cellInfo = [self.objectValue lineInfo]; - float pathWidth = 0; - if (cellInfo && ![controller hasNonlinearPath]) - pathWidth = 10 + 10 * cellInfo.numColumns; - - int index = 0; - NSRect refRect = NSMakeRect(pathWidth, 0, 1000, 10000); - for (NSValue *rectValue in [self rectsForRefsinRect:refRect]) - { - NSRect rect = [rectValue rectValue]; - if (x >= rect.origin.x && x <= (rect.origin.x + rect.size.width)) - return index; - ++index; - } - - return -1; -} - -- (NSRect) rectAtIndex:(int)index -{ - cellInfo = [self.objectValue lineInfo]; - float pathWidth = 0; - if (cellInfo && ![controller hasNonlinearPath]) - pathWidth = 10 + 10 * cellInfo.numColumns; - NSRect refRect = NSMakeRect(pathWidth, 0, 1000, 10000); - - return [[[self rectsForRefsinRect:refRect] objectAtIndex:index] rectValue]; -} - -# pragma mark context menu delegate methods - -- (NSMenu *) menuForEvent:(NSEvent *)event inRect:(NSRect)rect ofView:(NSView *)view -{ - if (!contextMenuDelegate) - return [self menu]; - - int i = [self indexAtX:[view convertPointFromBase:[event locationInWindow]].x]; - if (i < 0) - return [self menu]; - - id ref = [[[self objectValue] refs] objectAtIndex:i]; - if (!ref) - return [self menu]; - - NSArray *items = [contextMenuDelegate menuItemsForRef:ref commit:[self objectValue]]; - NSMenu *menu = [[NSMenu alloc] init]; - for (NSMenuItem *item in items) - [menu addItem:item]; - return menu; - - return [self menu]; -} -@end diff --git a/PBGitWindowController.h b/PBGitWindowController.h deleted file mode 100644 index ae2298b85..000000000 --- a/PBGitWindowController.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// PBDetailController.h -// GitX -// -// Created by Pieter de Bie on 16-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import "PBGitRepository.h" - -@class PBViewController; -@interface PBGitWindowController : NSWindowController { - __weak PBGitRepository* repository; - int selectedViewIndex; - IBOutlet NSView* contentView; - - PBViewController *historyViewController; - PBViewController *commitViewController; - - PBViewController* viewController; -} - -@property (assign) __weak PBGitRepository *repository; -@property (readonly) NSViewController *viewController; -@property (assign) int selectedViewIndex; - -- (id)initWithRepository:(PBGitRepository*)theRepository displayDefault:(BOOL)display; - -- (void)changeViewController:(NSInteger)whichViewTag; -- (void)useToolbar:(NSToolbar *)toolbar; -- (void)showMessageSheet:(NSString *)messageText infoText:(NSString *)infoText; -- (void)showErrorSheet:(NSError *)error; - -- (IBAction) showCommitView:(id)sender; -- (IBAction) showHistoryView:(id)sender; -@end diff --git a/PBGitWindowController.m b/PBGitWindowController.m deleted file mode 100644 index aba1987b0..000000000 --- a/PBGitWindowController.m +++ /dev/null @@ -1,159 +0,0 @@ -// -// PBDetailController.m -// GitX -// -// Created by Pieter de Bie on 16-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGitWindowController.h" -#import "PBGitHistoryController.h" -#import "PBGitCommitController.h" - -@implementation PBGitWindowController - - -@synthesize repository, viewController, selectedViewIndex; - -- (id)initWithRepository:(PBGitRepository*)theRepository displayDefault:(BOOL)displayDefault -{ - if(self = [self initWithWindowNibName:@"RepositoryWindow"]) - { - self.repository = theRepository; - [self showWindow:nil]; - } - - if (displayDefault) { - self.selectedViewIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"selectedViewIndex"]; - } else { - self.selectedViewIndex = -1; - } - - return self; -} - -- (void)windowWillClose:(NSNotification *)notification -{ - NSLog(@"Window will close!"); - if (historyViewController) - [historyViewController removeView]; - if (commitViewController) - [commitViewController removeView]; -} - -- (BOOL)validateMenuItem:(NSMenuItem *)menuItem -{ - if ([menuItem action] == @selector(showCommitView:) || [menuItem action] == @selector(showHistoryView:)) { - return ![repository isBareRepository]; - } - return YES; -} - -- (void) setSelectedViewIndex: (int) i -{ - [self changeViewController: i]; -} - -- (void)changeViewController:(NSInteger)whichViewTag -{ - [self willChangeValueForKey:@"viewController"]; - - if (viewController != nil) - [[viewController view] removeFromSuperview]; - - if ([repository isBareRepository]) { // in bare repository we don't want to view commit - whichViewTag = 0; // even if it was selected by default - } - - // Set our default here because we might have changed it (based on bare repo) before - selectedViewIndex = whichViewTag; - [[NSUserDefaults standardUserDefaults] setInteger:whichViewTag forKey:@"selectedViewIndex"]; - - switch (whichViewTag) - { - case 0: // swap in the "CustomImageViewController - NSImageView" - if (!historyViewController) - historyViewController = [[PBGitHistoryController alloc] initWithRepository:repository superController:self]; - else - [historyViewController updateView]; - viewController = historyViewController; - break; - case 1: - if (!commitViewController) - commitViewController = [[PBGitCommitController alloc] initWithRepository:repository superController:self]; - else - [commitViewController updateView]; - - viewController = commitViewController; - break; - } - - // make sure we automatically resize the controller's view to the current window size - [[viewController view] setFrame: [contentView bounds]]; - - //// embed the current view to our host view - [contentView addSubview: [viewController view]]; - - [self useToolbar: [viewController viewToolbar]]; - - // Allow the viewcontroller to catch actions - [self setNextResponder: viewController]; - [self didChangeValueForKey:@"viewController"]; // this will trigger the NSTextField's value binding to change - - [[self window] makeFirstResponder:[viewController firstResponder]]; -} - -- (void)awakeFromNib -{ - [[self window] setDelegate:self]; - [[self window] setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge]; - [[self window] setContentBorderThickness:35.0f forEdge:NSMinYEdge]; - [self showHistoryView:nil]; -} - -- (void) showCommitView:(id)sender -{ - if (self.selectedViewIndex != 1) - self.selectedViewIndex = 1; -} - -- (void) showHistoryView:(id)sender -{ - if (self.selectedViewIndex != 0) - self.selectedViewIndex = 0; -} - -- (void)showMessageSheet:(NSString *)messageText infoText:(NSString *)infoText -{ - [[NSAlert alertWithMessageText:messageText - defaultButton:nil - alternateButton:nil - otherButton:nil - informativeTextWithFormat:infoText] beginSheetModalForWindow: [self window] modalDelegate:self didEndSelector:nil contextInfo:nil]; -} - -- (void)showErrorSheet:(NSError *)error -{ - [[NSAlert alertWithError:error] beginSheetModalForWindow: [self window] modalDelegate:self didEndSelector:nil contextInfo:nil]; -} - - -#pragma mark - -#pragma mark Toolbar Delegates - -- (void) useToolbar:(NSToolbar *)toolbar -{ - NSSegmentedControl *item = nil; - for (NSToolbarItem *toolbarItem in [toolbar items]) { - if ([[toolbarItem view] isKindOfClass:[NSSegmentedControl class]]) { - item = (NSSegmentedControl *)[toolbarItem view]; - break; - } - } - [item bind:@"selectedIndex" toObject:self withKeyPath:@"selectedViewIndex" options:0]; - [item setEnabled: ![repository isBareRepository]]; - - [self.window setToolbar:toolbar]; -} - -@end diff --git a/PBGraphCellInfo.m b/PBGraphCellInfo.m deleted file mode 100644 index e5002cfa0..000000000 --- a/PBGraphCellInfo.m +++ /dev/null @@ -1,28 +0,0 @@ -// -// PBGraphCellInfo.m -// GitX -// -// Created by Pieter de Bie on 27-08-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBGraphCellInfo.h" - - -@implementation PBGraphCellInfo -@synthesize lines, position, numColumns, sign, nLines; -- (id)initWithPosition:(int)p andLines:(struct PBGitGraphLine *)l -{ - position = p; - lines = l; - - return self; -} - --(void) finalize -{ - free(lines); - [super finalize]; -} - -@end \ No newline at end of file diff --git a/PBNiceSplitView.h b/PBNiceSplitView.h deleted file mode 100644 index f5969e71d..000000000 --- a/PBNiceSplitView.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// PBNiceSplitView.h -// GitX -// -// Created by Pieter de Bie on 31-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import - -@interface PBNiceSplitView : NSSplitView { - -} - -@end diff --git a/PBNiceSplitView.m b/PBNiceSplitView.m deleted file mode 100644 index 994e6d4c0..000000000 --- a/PBNiceSplitView.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// PBNiceSplitView.m -// GitX -// -// Created by Pieter de Bie on 31-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import "PBNiceSplitView.h" - -static NSImage *bar; -static NSImage *grip; - -@implementation PBNiceSplitView - -+(void) initialize -{ - NSString *barPath = [[NSBundle mainBundle] pathForResource:@"mainSplitterBar" ofType:@"tiff"]; - bar = [[NSImage alloc] initWithContentsOfFile: barPath]; - [bar setFlipped: YES]; - - NSString *gripPath = [[NSBundle mainBundle] pathForResource:@"mainSplitterDimple" ofType:@"tiff"]; - grip = [[NSImage alloc] initWithContentsOfFile: gripPath]; - [grip setFlipped: YES]; -} - -- (void)drawDividerInRect:(NSRect)aRect -{ - // Draw bar and grip onto the canvas - NSRect gripRect = aRect; - gripRect.origin.x = (NSMidX(aRect) - ([grip size].width/2)); - gripRect.size.width = 8; - - [self lockFocus]; - [bar drawInRect:aRect fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0]; - [grip drawInRect:gripRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; - [self unlockFocus]; -} - -- (CGFloat)dividerThickness -{ - return 10.0; -} - -@end diff --git a/PBPrefsWindowController.h b/PBPrefsWindowController.h deleted file mode 100644 index ed65d38c4..000000000 --- a/PBPrefsWindowController.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// PBPrefsWindowController.h -// GitX -// -// Created by Christian Jacobsen on 02/10/2008. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import "DBPrefsWindowController.h" - -@interface PBPrefsWindowController : DBPrefsWindowController { - /* Outlets for Preference Views */ - IBOutlet NSView *generalPrefsView; - IBOutlet NSView *integrationPrefsView; - IBOutlet NSView *updatesPrefsView; - - /* Variables for the Updates View */ - IBOutlet NSPathControl *gitPathController; - IBOutlet NSImageView *badGitPathIcon; - IBOutlet NSView *gitPathOpenAccessory; - NSOpenPanel *gitPathOpenPanel; - -} - -- (IBAction) checkGitValidity: sender; -- (void)pathCell:(NSPathCell *)pathCell willDisplayOpenPanel:(NSOpenPanel *)openPanel; -- (IBAction) showHideAllFiles: sender; -- (IBAction) resetGitPath: sender; - -@end diff --git a/PBPrefsWindowController.m b/PBPrefsWindowController.m deleted file mode 100644 index 254adc4ec..000000000 --- a/PBPrefsWindowController.m +++ /dev/null @@ -1,62 +0,0 @@ -// -// PBPrefsWindowController.m -// GitX -// -// Created by Christian Jacobsen on 02/10/2008. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBPrefsWindowController.h" -#import "PBGitRepository.h" - -@implementation PBPrefsWindowController - -# pragma mark DBPrefsWindowController overrides - -- (void)setupToolbar -{ - // GENERAL - [self addView:generalPrefsView label:@"General" image:[NSImage imageNamed:@"gitx"]]; - // INTERGRATION - [self addView:integrationPrefsView label:@"Integration" image:[NSImage imageNamed:NSImageNameNetwork]]; - // UPDATES - [self addView:updatesPrefsView label:@"Updates"]; -} - -#pragma mark - -#pragma mark Delegate methods - -- (IBAction) checkGitValidity: sender -{ - // FIXME: This does not work reliably, probably due to: http://www.cocoabuilder.com/archive/message/cocoa/2008/9/10/217850 - //[badGitPathIcon setHidden:[PBGitRepository validateGit:[[NSValueTransformer valueTransformerForName:@"PBNSURLPathUserDefaultsTransfomer"] reverseTransformedValue:[gitPathController URL]]]]; -} - -- (IBAction) resetGitPath: sender -{ - [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"gitExecutable"]; -} - -- (void)pathCell:(NSPathCell *)pathCell willDisplayOpenPanel:(NSOpenPanel *)openPanel -{ - [openPanel setCanChooseDirectories:NO]; - [openPanel setCanChooseFiles:YES]; - [openPanel setAllowsMultipleSelection:NO]; - [openPanel setTreatsFilePackagesAsDirectories:YES]; - [openPanel setAccessoryView:gitPathOpenAccessory]; - //[[openPanel _navView] setShowsHiddenFiles:YES]; - - gitPathOpenPanel = openPanel; -} - -#pragma mark - -#pragma mark Git Path open panel actions - -- (IBAction) showHideAllFiles: sender -{ - /* FIXME: This uses undocumented OpenPanel features to show hidden files! */ - NSNumber *showHidden = [NSNumber numberWithBool:[sender state] == NSOnState]; - [[gitPathOpenPanel valueForKey:@"_navView"] setValue:showHidden forKey:@"showsHiddenFiles"]; -} - -@end diff --git a/PBRefContextDelegate.h b/PBRefContextDelegate.h deleted file mode 100644 index 81019ce33..000000000 --- a/PBRefContextDelegate.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// PBRefContextDelegate.m -// GitX -// -// Created by Pieter de Bie on 01-11-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - - - -@protocol PBRefContextDelegate -- (NSArray *) menuItemsForRef:(PBGitRef *)ref commit:(PBGitCommit *)commit; -@end diff --git a/PBRefController.h b/PBRefController.h deleted file mode 100644 index 0706fc5c9..000000000 --- a/PBRefController.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// PBLabelController.h -// GitX -// -// Created by Pieter de Bie on 21-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import -#import "PBGitHistoryController.h" -#import "PBCommitList.h" -#import "PBGitRef.h" -#import "PBGitCommit.h" -#import "PBRefContextDelegate.h" - -@interface PBRefController : NSObject { - IBOutlet __weak PBGitHistoryController *historyController; - IBOutlet NSArrayController *commitController; - IBOutlet PBCommitList *commitList; - - IBOutlet NSWindow *newBranchSheet; - IBOutlet NSTextField *newBranchName; - IBOutlet NSTextField *errorMessage; - - IBOutlet NSPopUpButton *branchPopUp; -} - -- (IBAction)addRef:(id)sender; -- (IBAction)closeSheet:(id) sender; -- (IBAction)saveSheet:(id) sender; - -- (NSArray *) menuItemsForRef:(PBGitRef *)ref commit:(PBGitCommit *)commit; - -- (void) changeBranch:(NSMenuItem *)sender; -- (void) selectCurrentBranch; -- (void) updateBranchMenu; - -@end diff --git a/PBRefController.m b/PBRefController.m deleted file mode 100644 index 31c55905c..000000000 --- a/PBRefController.m +++ /dev/null @@ -1,328 +0,0 @@ -// -// PBLabelController.m -// GitX -// -// Created by Pieter de Bie on 21-10-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import "PBRefController.h" -#import "PBGitRevisionCell.h" -#import "PBRefMenuItem.h" - -@implementation PBRefController - -- (void)awakeFromNib -{ - [commitList registerForDraggedTypes:[NSArray arrayWithObject:@"PBGitRef"]]; - [historyController addObserver:self forKeyPath:@"repository.branches" options:0 context:@"branchChange"]; - [historyController addObserver:self forKeyPath:@"repository.currentBranch" options:0 context:@"currentBranchChange"]; - [self updateBranchMenu]; - [self selectCurrentBranch]; -} - -- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if ([(NSString *)context isEqualToString: @"branchChange"]) { - [self updateBranchMenu]; - } - else if ([(NSString *)context isEqualToString:@"currentBranchChange"]) { - [self selectCurrentBranch]; - } - else { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - - -- (void) removeRef:(PBRefMenuItem *) sender -{ - NSString *ref_desc = [NSString stringWithFormat:@"%@ %@", [[sender ref] type], [[sender ref] shortName]]; - NSString *question = [NSString stringWithFormat:@"Are you sure you want to remove the %@?", ref_desc]; - int choice = NSRunAlertPanel([NSString stringWithFormat:@"Delete %@?", ref_desc], question, @"Delete", @"Cancel", nil); - // TODO: Use a non-modal alert here, so we don't block all the GitX windows - - if(choice) { - int ret = 1; - [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-d", [[sender ref] ref], nil] retValue: &ret]; - if (ret) { - NSLog(@"Removing ref failed!"); - return; - } - [historyController.repository removeBranch:[[PBGitRevSpecifier alloc] initWithRef:[sender ref]]]; - [[sender commit] removeRef:[sender ref]]; - [commitController rearrangeObjects]; - } -} - -- (void) checkoutRef:(PBRefMenuItem *)sender -{ - int ret = 1; - [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"checkout", [[sender ref] shortName], nil] retValue: &ret]; - if (ret) { - [[historyController.repository windowController] showMessageSheet:@"Checking out branch failed" infoText:@"There was an error checking out the branch. Perhaps your working directory is not clean?"]; - return; - } - [historyController.repository reloadRefs]; - [commitController rearrangeObjects]; -} - -- (void) tagInfo:(PBRefMenuItem *)sender -{ - NSString *message = [NSString stringWithFormat:@"Info for tag: %@", [[sender ref] shortName]]; - - int ret = 1; - NSString *info = [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"tag", @"-n50", @"-l", [[sender ref] shortName], nil] retValue: &ret]; - - if (!ret) { - [[historyController.repository windowController] showMessageSheet:message infoText:info]; - } - return; -} - -- (NSArray *) menuItemsForRef:(PBGitRef *)ref commit:(PBGitCommit *)commit -{ - return [PBRefMenuItem defaultMenuItemsForRef:ref commit:commit target:self]; -} - -# pragma mark Tableview delegate methods - -- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard -{ - NSPoint location = [tv convertPointFromBase:[(PBCommitList *)tv mouseDownPoint]]; - int row = [tv rowAtPoint:location]; - int column = [tv columnAtPoint:location]; - if (column != 0) - return NO; - - PBGitRevisionCell *cell = (PBGitRevisionCell *)[tv preparedCellAtColumn:column row:row]; - - int index = [cell indexAtX:location.x]; - - if (index == -1) - return NO; - - NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:row], [NSNumber numberWithInt:index], NULL]]; - [pboard declareTypes:[NSArray arrayWithObject:@"PBGitRef"] owner:self]; - [pboard setData:data forType:@"PBGitRef"]; - - return YES; -} - -- (NSDragOperation)tableView:(NSTableView*)tv - validateDrop:(id )info - proposedRow:(NSInteger)row - proposedDropOperation:(NSTableViewDropOperation)operation -{ - if (operation == NSTableViewDropAbove) - return NSDragOperationNone; - - NSPasteboard *pboard = [info draggingPasteboard]; - if ([pboard dataForType:@"PBGitRef"]) - return NSDragOperationMove; - - return NSDragOperationNone; -} - -- (BOOL)tableView:(NSTableView *)aTableView - acceptDrop:(id )info - row:(NSInteger)row - dropOperation:(NSTableViewDropOperation)operation -{ - if (operation != NSTableViewDropOn) - return NO; - - NSPasteboard *pboard = [info draggingPasteboard]; - NSData *data = [pboard dataForType:@"PBGitRef"]; - if (!data) - return NO; - - NSArray *numbers = [NSKeyedUnarchiver unarchiveObjectWithData:data]; - int oldRow = [[numbers objectAtIndex:0] intValue]; - int oldRefIndex = [[numbers objectAtIndex:1] intValue]; - PBGitCommit *oldCommit = [[commitController arrangedObjects] objectAtIndex: oldRow]; - PBGitRef *ref = [[oldCommit refs] objectAtIndex:oldRefIndex]; - - PBGitCommit *dropCommit = [[commitController arrangedObjects] objectAtIndex:row]; - - int a = [[NSAlert alertWithMessageText:@"Change branch" - defaultButton:@"Change" - alternateButton:@"Cancel" - otherButton:nil - informativeTextWithFormat:@"Do you want to change branch\n\n\t'%@'\n\n to point to commit\n\n\t'%@'", [ref shortName], [dropCommit subject]] runModal]; - if (a != NSAlertDefaultReturn) - return NO; - - int retValue = 1; - [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mUpdate from GitX", [ref ref], [dropCommit realSha], NULL] retValue:&retValue]; - if (retValue) - return NO; - - [dropCommit addRef:ref]; - [oldCommit removeRef:ref]; - - [commitController rearrangeObjects]; - [aTableView needsToDrawRect:[aTableView rectOfRow:oldRow]]; - return YES; -} - -# pragma mark Add ref methods --(void)addRef:(id)sender -{ - [errorMessage setStringValue:@""]; - [NSApp beginSheet:newBranchSheet - modalForWindow:[[historyController view] window] - modalDelegate:NULL - didEndSelector:NULL - contextInfo:NULL]; -} - --(void)saveSheet:(id) sender -{ - NSString *branchName = [@"refs/heads/" stringByAppendingString:[newBranchName stringValue]]; - - if ([[commitController selectedObjects] count] == 0) - return; - - PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0]; - - int retValue = 1; - [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"check-ref-format", branchName, nil] retValue:&retValue]; - if (retValue != 0) { - [errorMessage setStringValue:@"Invalid name"]; - return; - } - - retValue = 1; - [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mCreate branch from GitX", branchName, [commit realSha], @"0000000000000000000000000000000000000000", NULL] retValue:&retValue]; - if (retValue) - { - [errorMessage setStringValue:@"Branch exists"]; - return; - } - [historyController.repository addBranch:[[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:branchName]]]; - [self closeSheet:sender]; - [commit addRef:[PBGitRef refFromString:branchName]]; - [commitController rearrangeObjects]; -} - --(void)closeSheet:(id) sender -{ - [NSApp endSheet:newBranchSheet]; - [newBranchName setStringValue:@""]; - [newBranchSheet orderOut:self]; -} - -# pragma mark Branches menu - -- (void) updateBranchMenu -{ - if (!branchPopUp) - return; - - NSMutableArray *localBranches = [NSMutableArray array]; - NSMutableArray *remoteBranches = [NSMutableArray array]; - NSMutableArray *tags = [NSMutableArray array]; - NSMutableArray *other = [NSMutableArray array]; - - NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Branch menu"]; - for (PBGitRevSpecifier *rev in historyController.repository.branches) - { - if (![rev isSimpleRef]) - { - [other addObject:rev]; - continue; - } - - NSString *ref = [rev simpleRef]; - - if ([ref hasPrefix:@"refs/heads"]) - [localBranches addObject:rev]; - else if ([ref hasPrefix:@"refs/tags"]) - [tags addObject:rev]; - else if ([ref hasPrefix:@"refs/remote"]) - [remoteBranches addObject:rev]; - } - - for (PBGitRevSpecifier *rev in localBranches) - { - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""]; - [item setRepresentedObject:rev]; - [item setTarget:self]; - [menu addItem:item]; - } - - [menu addItem:[NSMenuItem separatorItem]]; - - // Remotes - NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Remotes"]; - NSMenu *currentMenu = NULL; - for (PBGitRevSpecifier *rev in remoteBranches) - { - NSString *ref = [rev simpleRef]; - NSArray *components = [ref componentsSeparatedByString:@"/"]; - - NSString *remoteName = [components objectAtIndex:2]; - NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"]; - - if (![[currentMenu title] isEqualToString:remoteName]) - { - currentMenu = [[NSMenu alloc] initWithTitle:remoteName]; - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""]; - [item setSubmenu:currentMenu]; - [remoteMenu addItem:item]; - } - - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(changeBranch:) keyEquivalent:@""]; - [item setTarget:self]; - [item setRepresentedObject:rev]; - [currentMenu addItem:item]; - } - - NSMenuItem *remoteItem = [[NSMenuItem alloc] initWithTitle:@"Remotes" action:NULL keyEquivalent:@""]; - [remoteItem setSubmenu:remoteMenu]; - [menu addItem:remoteItem]; - - // Tags - NSMenu *tagMenu = [[NSMenu alloc] initWithTitle:@"Tags"]; - for (PBGitRevSpecifier *rev in tags) - { - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""]; - [item setTarget:self]; - [item setRepresentedObject:rev]; - [tagMenu addItem:item]; - } - - NSMenuItem *tagItem = [[NSMenuItem alloc] initWithTitle:@"Tags" action:NULL keyEquivalent:@""]; - [tagItem setSubmenu:tagMenu]; - [menu addItem:tagItem]; - - - // Others - [menu addItem:[NSMenuItem separatorItem]]; - - for (PBGitRevSpecifier *rev in other) - { - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""]; - [item setRepresentedObject:rev]; - [item setTarget:self]; - [menu addItem:item]; - } - - [[branchPopUp cell] setMenu: menu]; -} - -- (void) changeBranch:(NSMenuItem *)sender -{ - PBGitRevSpecifier *rev = [sender representedObject]; - historyController.repository.currentBranch = rev; -} - -- (void) selectCurrentBranch -{ - PBGitRevSpecifier *rev = historyController.repository.currentBranch; - if (rev) - [branchPopUp setTitle:[rev description]]; -} - -@end diff --git a/PBRefMenuItem.h b/PBRefMenuItem.h deleted file mode 100644 index 0e3f4558a..000000000 --- a/PBRefMenuItem.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// PBRefMenuItem.h -// GitX -// -// Created by Pieter de Bie on 01-11-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import -#import "PBGitRef.h" -#import "PBGitCommit.h" - -@interface PBRefMenuItem : NSMenuItem { - PBGitRef *ref; - PBGitCommit *commit; -} - -@property (retain) PBGitCommit *commit; -@property (retain) PBGitRef *ref; - -+ (NSArray *)defaultMenuItemsForRef:(PBGitRef *)ref commit:(PBGitCommit *)commit target:(id)target; - -@end diff --git a/PBRefMenuItem.m b/PBRefMenuItem.m deleted file mode 100644 index 22aaae5a7..000000000 --- a/PBRefMenuItem.m +++ /dev/null @@ -1,46 +0,0 @@ -// -// PBRefMenuItem.m -// GitX -// -// Created by Pieter de Bie on 01-11-08. -// Copyright 2008 Pieter de Bie. All rights reserved. -// - -#import "PBRefMenuItem.h" - - -@implementation PBRefMenuItem -@synthesize ref, commit; - -+ (NSArray *)defaultMenuItemsForRef:(PBGitRef *)ref commit:(PBGitCommit *)commit target:(id)target -{ - NSMutableArray *array = [NSMutableArray array]; - NSString *type = [ref type]; - if ([type isEqualToString:@"remote"]) - type = @"remote branch"; - else if ([type isEqualToString:@"head"]) - type = @"branch"; - - [array addObject:[[PBRefMenuItem alloc] initWithTitle:[@"Delete " stringByAppendingString:type] - action:@selector(removeRef:) - keyEquivalent: @""]]; - if ([type isEqualToString:@"branch"]) - [array addObject:[[PBRefMenuItem alloc] initWithTitle:@"Checkout branch" - action:@selector(checkoutRef:) - keyEquivalent: @""]]; - - if ([type isEqualToString:@"tag"]) - [array addObject:[[PBRefMenuItem alloc] initWithTitle:@"View tag info" - action:@selector(tagInfo:) - keyEquivalent: @""]]; - - for (PBRefMenuItem *item in array) - { - [item setTarget: target]; - [item setRef: ref]; - [item setCommit:commit]; - } - - return array; -} -@end diff --git a/PBRepositoryDocumentController.m b/PBRepositoryDocumentController.m deleted file mode 100644 index 83bd887e9..000000000 --- a/PBRepositoryDocumentController.m +++ /dev/null @@ -1,81 +0,0 @@ -// -// PBRepositoryDocumentController.mm -// GitX -// -// Created by Ciarán Walsh on 15/08/2008. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBRepositoryDocumentController.h" -#import "PBGitRepository.h" -#import "PBGitRevList.h" - -@implementation PBRepositoryDocumentController -// This method is overridden to configure the open panel to only allow -// selection of directories -- (NSInteger)runModalOpenPanel:(NSOpenPanel *)openPanel forTypes:(NSArray *)extensions -{ - [openPanel setCanChooseFiles:YES]; - [openPanel setCanChooseDirectories:YES]; - return [openPanel runModalForDirectory:nil file:nil types:[NSArray arrayWithObject: @"git"]]; -} - -// Convert paths to the .git dir before searching for an already open document -- (id)documentForURL:(NSURL *)URL -{ - return [super documentForURL:[PBGitRepository gitDirForURL:URL]]; -} - -- (void)noteNewRecentDocumentURL:(NSURL*)url -{ - [super noteNewRecentDocumentURL:[PBGitRepository baseDirForURL:url]]; -} - -- (id) documentForLocation:(NSURL*) url -{ - id document = [self documentForURL:url]; - if (!document) { - - if (!(document = [[PBGitRepository alloc] initWithURL:url])) - return nil; - - [self addDocument:document]; - } - else - [document showWindows]; - - return document; -} - - -- (IBAction)newDocument:(id)sender -{ - NSOpenPanel *op = [NSOpenPanel openPanel]; - - [op setCanChooseFiles:NO]; - [op setCanChooseDirectories:YES]; - [op setAllowsMultipleSelection:NO]; - [op setMessage:@"Initialize a repository here:"]; - [op setTitle:@"New Repository"]; - if ([op runModal] == NSFileHandlingPanelOKButton) - { - NSString *path = [op filename]; - int terminationStatus; - NSString *result = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:[NSArray arrayWithObjects:@"init", @"-q", nil] inDir:path inputString:nil retValue:&terminationStatus]; - - if (terminationStatus == 0) - [self openDocumentWithContentsOfURL:[op URL] display:YES error:NULL]; - else - NSRunAlertPanel(@"Failed to create new Git repository", @"Git returned the following error when trying to create the repository: %@", nil, nil, nil, result); - } -} - - -- (BOOL)validateMenuItem:(NSMenuItem *)item -{ - if ([item action] == @selector(newDocument:)) - return ([PBGitBinary path] != nil); - return [super validateMenuItem:item]; -} - -@end diff --git a/PBServicesController.m b/PBServicesController.m deleted file mode 100644 index dd1830b42..000000000 --- a/PBServicesController.m +++ /dev/null @@ -1,59 +0,0 @@ -// -// PBServicesController.m -// GitX -// -// Created by Pieter de Bie on 10/24/08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBServicesController.h" -#import "PBRepositoryDocumentController.h" -#import "PBGitRepository.h" - -@implementation PBServicesController - -- (NSString *)completeSHA1For:(NSString *)sha -{ - NSArray *documents = [[NSApplication sharedApplication] orderedDocuments]; - for (PBGitRepository *repo in documents) - { - int ret = 1; - NSString *s = [repo outputForArguments:[NSArray arrayWithObjects:@"log", @"-1", @"--pretty=format:%h (%s)", sha, nil] retValue:&ret]; - if (!ret) - return s; - } - return @"Could not find SHA"; -} - --(NSString *)runNameRevFor:(NSString *)s -{ - NSArray *repositories = [[NSApplication sharedApplication] orderedDocuments]; - if ([repositories count] == 0) - return s; - PBGitRepository *repo = [repositories objectAtIndex:0]; - int ret = 1; - NSString *returnString = [repo outputForArguments:[NSArray arrayWithObjects:@"name-rev", @"--stdin", nil] inputString:s retValue:&ret]; - if (ret) - return s; - return returnString; -} - --(void)completeSha:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error -{ - NSArray *types = [pboard types]; - if (![types containsObject:NSStringPboardType]) - { - *error = @"Could not get data"; - return; - } - - NSString *s = [pboard stringForType:NSStringPboardType]; - if ([s rangeOfString:@" "].location == NSNotFound) - s = [self completeSHA1For:s]; - else - s = [self runNameRevFor:s]; - - [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - [pboard setString:s forType:NSStringPboardType]; -} -@end diff --git a/PBViewController.h b/PBViewController.h deleted file mode 100644 index 4715c61d4..000000000 --- a/PBViewController.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// PBViewController.h -// GitX -// -// Created by Pieter de Bie on 22-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import -#import "PBGitRepository.h" -#import "PBGitWindowController.h" - -@interface PBViewController : NSViewController { - __weak PBGitRepository *repository; - __weak PBGitWindowController *superController; - - IBOutlet NSToolbar *viewToolbar; -} - -@property (readonly) __weak PBGitRepository *repository; -@property (readonly) NSToolbar *viewToolbar; - -- (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller; -- (void) removeView; -- (void) updateView; -- (NSResponder *)firstResponder; - -@end diff --git a/PBWebChangesController.m b/PBWebChangesController.m deleted file mode 100644 index 933fecc22..000000000 --- a/PBWebChangesController.m +++ /dev/null @@ -1,111 +0,0 @@ -// -// PBWebChangesController.m -// GitX -// -// Created by Pieter de Bie on 22-09-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBWebChangesController.h" -#import "PBGitIndexController.h" -#import "PBGitIndex.h" - -@implementation PBWebChangesController - -- (void) awakeFromNib -{ - selectedFile = nil; - selectedFileIsCached = NO; - - startFile = @"commit"; - [super awakeFromNib]; - - [unstagedFilesController addObserver:self forKeyPath:@"selection" options:0 context:@"UnstagedFileSelected"]; - [cachedFilesController addObserver:self forKeyPath:@"selection" options:0 context:@"cachedFileSelected"]; -} - -- (void) didLoad -{ - [[self script] setValue:controller.index forKey:@"Index"]; - [self refresh]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - NSArrayController *otherController; - otherController = object == unstagedFilesController ? cachedFilesController : unstagedFilesController; - int count = [[object selectedObjects] count]; - if (count == 0) { - if([[otherController selectedObjects] count] == 0 && selectedFile) { - selectedFile = nil; - selectedFileIsCached = NO; - [self refresh]; - } - return; - } - - // TODO: Move this to commitcontroller - [otherController setSelectionIndexes:[NSIndexSet indexSet]]; - - if (count > 1) { - [self showMultiple: [object selectedObjects]]; - return; - } - - selectedFile = [[object selectedObjects] objectAtIndex:0]; - selectedFileIsCached = object == cachedFilesController; - - [self refresh]; -} - -- (void) showMultiple: (NSArray *)objects -{ - [[self script] callWebScriptMethod:@"showMultipleFilesSelection" withArguments:[NSArray arrayWithObject:objects]]; -} - -- (void) refresh -{ - if (!finishedLoading) - return; - - id script = [view windowScriptObject]; - [script callWebScriptMethod:@"showFileChanges" - withArguments:[NSArray arrayWithObjects:selectedFile ?: (id)[NSNull null], - [NSNumber numberWithBool:selectedFileIsCached], nil]]; -} - -- (void)stageHunk:(NSString *)hunk reverse:(BOOL)reverse -{ - [controller.index applyPatch:hunk stage:YES reverse:reverse]; - // FIXME: Don't need a hard refresh - - [self refresh]; -} - -- (void)discardHunk:(NSString *)hunk altKey:(BOOL)altKey -{ - int ret = NSAlertDefaultReturn; - if (!altKey) { - ret = [[NSAlert alertWithMessageText:@"Discard hunk" - defaultButton:nil - alternateButton:@"Cancel" - otherButton:nil - informativeTextWithFormat:@"Are you sure you wish to discard the changes in this hunk?\n\nYou cannot undo this operation."] runModal]; - } - - if (ret == NSAlertDefaultReturn) { - [controller.index applyPatch:hunk stage:NO reverse:YES]; - [self refresh]; - } -} - -- (void) setStateMessage:(NSString *)state -{ - id script = [view windowScriptObject]; - [script callWebScriptMethod:@"setState" withArguments: [NSArray arrayWithObject:state]]; -} - -@end diff --git a/PBWebHistoryController.m b/PBWebHistoryController.m deleted file mode 100644 index 1934b7353..000000000 --- a/PBWebHistoryController.m +++ /dev/null @@ -1,158 +0,0 @@ -// -// PBWebGitController.m -// GitTest -// -// Created by Pieter de Bie on 14-06-08. -// Copyright 2008 __MyCompanyName__. All rights reserved. -// - -#import "PBWebHistoryController.h" -#import "PBGitDefaults.h" - -@implementation PBWebHistoryController - -@synthesize diff; - -- (void) awakeFromNib -{ - startFile = @"history"; - repository = historyController.repository; - [super awakeFromNib]; - [historyController addObserver:self forKeyPath:@"webCommit" options:0 context:@"ChangedCommit"]; -} - -- (void) didLoad -{ - currentSha = @""; - [self changeContentTo: historyController.webCommit]; -} - -- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if ([(NSString *)context isEqualToString: @"ChangedCommit"]) - [self changeContentTo: historyController.webCommit]; - else - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; -} - -- (void) changeContentTo: (PBGitCommit *) content -{ - if (content == nil || !finishedLoading) - return; - - // The sha is the same, but refs may have changed.. reload it lazy - if ([currentSha isEqualToString: [content realSha]]) - { - [[self script] callWebScriptMethod:@"reload" withArguments: nil]; - return; - } - currentSha = [content realSha]; - - NSArray *arguments = [NSArray arrayWithObjects:content, [[[historyController repository] headRef] simpleRef], nil]; - [[self script] callWebScriptMethod:@"loadCommit" withArguments: arguments]; - - // Now we load the extended details. We used to do this in a separate thread, - // but this caused some funny behaviour because NSTask's and NSThread's don't really - // like each other. Instead, just do it async. - - NSMutableArray *taskArguments = [NSMutableArray arrayWithObjects:@"show", @"--pretty=raw", @"-M", @"--no-color", currentSha, nil]; - if (![PBGitDefaults showWhitespaceDifferences]) - [taskArguments insertObject:@"-w" atIndex:1]; - - NSFileHandle *handle = [repository handleForArguments:taskArguments]; - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - // Remove notification, in case we have another one running - [nc removeObserver:self]; - [nc addObserver:self selector:@selector(commitDetailsLoaded:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - [handle readToEndOfFileInBackgroundAndNotify]; -} - -- (void)commitDetailsLoaded:(NSNotification *)notification -{ - NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem]; - if (!data) - return; - - NSString *details = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (!details) - details = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; - - if (!details) - return; - - [[view windowScriptObject] callWebScriptMethod:@"loadCommitDetails" withArguments:[NSArray arrayWithObject:details]]; -} - -- (void) selectCommit: (NSString*) sha -{ - [historyController selectCommit:sha]; -} - -- (void) sendKey: (NSString*) key -{ - id script = [view windowScriptObject]; - [script callWebScriptMethod:@"handleKeyFromCocoa" withArguments: [NSArray arrayWithObject:key]]; -} - -- (void) copySource -{ - NSString *source = [(DOMHTMLElement *)[[[view mainFrame] DOMDocument] documentElement] outerHTML]; - NSPasteboard *a =[NSPasteboard generalPasteboard]; - [a declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; - [a setString:source forType: NSStringPboardType]; -} - -- (NSArray *) webView:(WebView *)sender -contextMenuItemsForElement:(NSDictionary *)element - defaultMenuItems:(NSArray *)defaultMenuItems -{ - DOMNode *node = [element valueForKey:@"WebElementDOMNode"]; - - while (node) { - // Every ref has a class name of 'refs' and some other class. We check on that to see if we pressed on a ref. - if ([[node className] hasPrefix:@"refs "]) { - NSString *selectedRefString = [[[node childNodes] item:0] textContent]; - for (PBGitRef *ref in historyController.webCommit.refs) - { - if ([[ref shortName] isEqualToString:selectedRefString]) - return [contextMenuDelegate menuItemsForRef:ref commit:historyController.webCommit]; - } - NSLog(@"Could not find selected ref!"); - return defaultMenuItems; - } - if ([node hasAttributes] && [[node attributes] getNamedItem:@"representedFile"]) - return [historyController menuItemsForPaths:[NSArray arrayWithObject:[[[node attributes] getNamedItem:@"representedFile"] value]]]; - - node = [node parentNode]; - } - - return defaultMenuItems; -} - - -// Open external links in the default browser -- (void)webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)actionInformation - request:(NSURLRequest *)request - newFrameName:(NSString *)frameName - decisionListener:(id < WebPolicyDecisionListener >)listener -{ - [[NSWorkspace sharedWorkspace] openURL:[request URL]]; -} - -- getConfig:(NSString *)config -{ - return [historyController valueForKeyPath:[@"repository.config." stringByAppendingString:config]]; -} - -- (void) finalize -{ - [historyController removeObserver:self forKeyPath:@"webCommit"]; - [super finalize]; -} - -- (void) preferencesChanged -{ - [[self script] callWebScriptMethod:@"enableFeatures" withArguments:nil]; -} - -@end diff --git a/README b/README deleted file mode 100644 index b87b56637..000000000 --- a/README +++ /dev/null @@ -1,68 +0,0 @@ -GitX ---------------- - -# What is GitX? - -GitX is a gitk like clone written specifically for OS X Leopard and higher. -This means that it has a native interface and tries to integrate with the -operating system as good as possible. Examples of this are drag and drop -support and QuickLook support. - - -# Features - -The project is currently still in its starting phases. As time goes on, -hopefully more features will be added. Currently GitX supports the following: - - * History browsing of your repository - * See a nicely formatted diff of any revision - * Search based on author or revision subject - * Look at the complete tree of any revision - * Preview any file in the tree in a text view or with QuickLook - * Drag and drop files out of the tree view to copy them to your system - * Support for all parameters git rev-list has -# License - -GitX is licensed under the GPL version 2. For more information, see the attached COPYING file. - -# Downloading - -GitX is currently hosted at GitHub. It's project page can be found at -http://github.com/pieter/gitx. Recent binary releases can be found at -http://github.com/pieter/gitx/wikis. - -If you wish to follow GitX development, you can download the source code -through git: - - git clone git://github.com/pieter/gitx - -# Installation - -The easiest way to get GitX running is to download the binary release from the -wiki. If you wish to compile it yourself, you will need XCode 3.0 or later. As -GitX makes use of features available only on Leopard (such as garbage -collection), you will not be able to compile it on previous versions of OS X. - -To compile GitX, open the GitX.xcodeproj file and hit "Build". - -# Usage - -GitX itself is fairly simple. Most of its power is in the 'gitx' binary, which -you should install through the menu. the 'gitx' binary supports most of git -rev-list's arguments. For example, you can run `gitx --all' to display all -branches in the repository, or `gitx -- Documentation' to only show commits -relating to the 'Documentation' subdirectory. With `gitx -Shaha', gitx will -only show commits that contain the word 'haha'. Similarly, with 'gitx -v0.2.1..', you will get a list of all commits since version 0.2.1. - -# Helping out - -Any help on GitX is welcome. GitX is programmed in Objective-C, but even if -you are not a programmer you can do useful things. A short selection: - - * Create a nice icon; - * Help with the Javascript/HTML views, such as the diff view; - * File bug reports and feature requests. - -A TODO list can be found on the wiki: http://github.com/pieter/gitx/wikis/todo - diff --git a/README.markdown b/README.markdown new file mode 100644 index 000000000..20d83cfd1 --- /dev/null +++ b/README.markdown @@ -0,0 +1,20 @@ +# Codebase GitX + +Vertical split, minimized UI, curated layout. **Dark theme only** + +[Binary downloads here](https://github.com/codebasesaga/gitx/releases). + +![Screenshot](Documentation/Screenshot.png) + +## Installing the Terminal tool + +UI removed. Make a symlink, or you won’t get updates: + + ln -s /Applications/GitX.app/Contents/Resources/gitx /usr/local/bin + +Or maybe just a bash-alias. + +## How to Build & Install: + +See [the wiki page](https://github.com/gitx/gitx/wiki/Build-instructions) +for build instructions. diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 028349282..000000000 --- a/Rakefile +++ /dev/null @@ -1,66 +0,0 @@ -require 'ftools' - -target_locations = [ - File::expand_path("~/Applications/"), - "/Applications/" -] - -desc "Build and install (or upgrade) GitX" -task :install => [:uninstall_app, :build_app, :install_app] -desc "Clean build directory, uninstall application" -task :uninstall => [:clean_app, :uninstall_app] -desc "Clean build directory" -task :clean => [:clean_app] - -desc "Build gitX using XCode" -task :build_app do - system("xcodebuild build OBJROOT=build/ SYMROOT=build/") -end - -task :clean_app do - system("xcodebuild -alltargets clean OBJROOT=build/ SYMROOT=build/") -end - -desc "Copies the built GitX.app to the application folder" -task :install_app do - target_locations.each do |loc| - if File.directory?(loc) - puts "Copying to (#{loc})" - system("cp -R build/Release/GitX.app #{loc}") - break - end - end -end - -desc "Remove GitX.app from ~/Applications/ or /Applications/" -task :uninstall_app do - found = false - target_locations.each do |loc| - cur_path = File.join(loc, "GitX.app") - puts "Checking #{cur_path}" - if File.exists?( cur_path ) - puts "Removing GitX.app from #{cur_path}" - system("rm", "-rf", cur_path) - found = true - break - end - end - puts "Couldn't find installed GitX.app" unless found -end - -desc "Creates a zip file with current GitX" -task :create_zip do - if ENV["STABLE"] - name = "GitXStable" - else - name = "Nightly" - end - - delete = File.directory?("build/Release") - system("xcodebuild") - system("cd build/Release && zip -r #{name}.app.zip GitX.app") - system("mv build/Release/#{name}.app.zip .") - system("rm -rf build/Release") if delete - system("scp #{name}.app.zip sydney:public_html/gitx/Downloads/") # This is a local script -- Pieter - puts "Uploaded to http://gitx.frim.nl/Downloads/#{name}.app.zip" -end diff --git a/Resources/Base.lproj/MainMenu.xib b/Resources/Base.lproj/MainMenu.xib new file mode 100644 index 000000000..4ec9877f2 --- /dev/null +++ b/Resources/Base.lproj/MainMenu.xib @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +DQ + + + + + + + +DQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Base.lproj/PBAddRemoteSheet.xib b/Resources/Base.lproj/PBAddRemoteSheet.xib new file mode 100644 index 000000000..5e6fa64ad --- /dev/null +++ b/Resources/Base.lproj/PBAddRemoteSheet.xib @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Examples: +/Users/username/path/to/repo.git/ +git://host.xz/path/to/repo.git/ +ssh://[user@]host.xz/path/to/repo.git/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Base.lproj/PBCloneRepositoryPanel.xib b/Resources/Base.lproj/PBCloneRepositoryPanel.xib new file mode 100644 index 000000000..706ae9a9c --- /dev/null +++ b/Resources/Base.lproj/PBCloneRepositoryPanel.xib @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Examples: +/Users/username/path/to/repo.git/ +git://host.xz/path/to/repo.git/ +ssh://[user@]host.xz/path/to/repo.git/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Base.lproj/PBCreateBranchSheet.xib b/Resources/Base.lproj/PBCreateBranchSheet.xib new file mode 100644 index 000000000..aaa77d1c1 --- /dev/null +++ b/Resources/Base.lproj/PBCreateBranchSheet.xib @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Base.lproj/PBCreateTagSheet.xib b/Resources/Base.lproj/PBCreateTagSheet.xib new file mode 100644 index 000000000..156d8c118 --- /dev/null +++ b/Resources/Base.lproj/PBCreateTagSheet.xib @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Base.lproj/PBRemoteProgressSheet.xib b/Resources/Base.lproj/PBRemoteProgressSheet.xib new file mode 100644 index 000000000..171d635ac --- /dev/null +++ b/Resources/Base.lproj/PBRemoteProgressSheet.xib @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Base.lproj/RepositoryWindow.xib b/Resources/Base.lproj/RepositoryWindow.xib new file mode 100644 index 000000000..963353cdc --- /dev/null +++ b/Resources/Base.lproj/RepositoryWindow.xib @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/English.lproj/InfoPlist.strings b/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 000000000..5ec04d1e5 --- /dev/null +++ b/Resources/English.lproj/InfoPlist.strings @@ -0,0 +1,3 @@ +/* Localized versions of Info.plist keys */ + +NSHumanReadableCopyright = "© Pieter de Bie, 2010\n© Codebase LLC, 2018"; diff --git a/Resources/GitX.entitlements b/Resources/GitX.entitlements new file mode 100644 index 000000000..0c67376eb --- /dev/null +++ b/Resources/GitX.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/GitX.h b/Resources/GitX.h new file mode 100644 index 000000000..2361a3e1e --- /dev/null +++ b/Resources/GitX.h @@ -0,0 +1,107 @@ +/* + * GitX.h + */ + +#import +#import + + +@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; + +@protocol GitXGenericMethods + +- (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. +- (void) searchString:(NSString *)string inMode:(NSInteger)inMode; // Highlight commits that match the given search string. + +@end + + + +/* + * Standard Suite + */ + +// The application's top-level scripting object. +@interface GitXApplication : SBApplication + +- (SBElementArray *) documents; +- (SBElementArray *) windows; + +@property (copy, readonly) NSString *name; // The name of the application. +@property (readonly) BOOL frontmost; // Is this the active application? +@property (copy, readonly) NSString *version; // The version number of 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) 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 + +// A document. +@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. + + +@end + +// A window. +@interface GitXWindow : SBObject + +@property (copy, readonly) NSString *name; // The title of the window. +- (NSInteger) id; // The unique identifier of the window. +@property NSInteger index; // The index of the window, ordered front to back. +@property NSRect bounds; // The bounding rectangle of the window. +@property (readonly) BOOL closeable; // Does the window have a close button? +@property (readonly) BOOL miniaturizable; // Does the window have a minimize button? +@property BOOL miniaturized; // Is the window minimized right now? +@property (readonly) BOOL resizable; // Can the window be resized? +@property BOOL visible; // Is the window visible right now? +@property (readonly) BOOL zoomable; // Does the window have a zoom button? +@property BOOL zoomed; // Is the window zoomed right now? +@property (copy, readonly) GitXDocument *document; // The document whose contents are displayed in the window. + + +@end + + + +/* + * GitX Suite + */ + +// The GitX application. +@interface GitXApplication (GitXSuite) + +@end + +// A document. +@interface GitXDocument (GitXSuite) + +@end + diff --git a/Resources/GitX.sdef b/Resources/GitX.sdef new file mode 100644 index 000000000..5fb7ac7aa --- /dev/null +++ b/Resources/GitX.sdef @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/GitX_Prefix.pch b/Resources/GitX_Prefix.pch new file mode 100644 index 000000000..fa2c2eb7d --- /dev/null +++ b/Resources/GitX_Prefix.pch @@ -0,0 +1,7 @@ +// + +#ifdef __OBJC__ + #import + #import + #import "PBMacros.h" +#endif diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/Contents.json b/Resources/Images.xcassets/AppIcon-gitx.appiconset/Contents.json new file mode 100644 index 000000000..7cd4f8e12 --- /dev/null +++ b/Resources/Images.xcassets/AppIcon-gitx.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16@2x.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32@2x.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128@2x.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256@2x.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_128x128.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_128x128.png new file mode 100644 index 000000000..3b7d0e25d Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_128x128.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_128x128@2x.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_128x128@2x.png new file mode 100644 index 000000000..37b6cc550 Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_128x128@2x.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_16x16.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_16x16.png new file mode 100644 index 000000000..1efef878d Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_16x16.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_16x16@2x.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_16x16@2x.png new file mode 100644 index 000000000..1fb00e815 Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_16x16@2x.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_256x256.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_256x256.png new file mode 100644 index 000000000..37b6cc550 Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_256x256.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_256x256@2x.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_256x256@2x.png new file mode 100644 index 000000000..cf5548990 Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_256x256@2x.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_32x32.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_32x32.png new file mode 100644 index 000000000..1fb00e815 Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_32x32.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_32x32@2x.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_32x32@2x.png new file mode 100644 index 000000000..b6ee4c24b Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_32x32@2x.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_512x512.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_512x512.png new file mode 100644 index 000000000..cf5548990 Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_512x512.png differ diff --git a/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_512x512@2x.png b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_512x512@2x.png new file mode 100644 index 000000000..792f90f4d Binary files /dev/null and b/Resources/Images.xcassets/AppIcon-gitx.appiconset/icon_512x512@2x.png differ diff --git a/Resources/Images.xcassets/Contents.json b/Resources/Images.xcassets/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Resources/Images/AddBranchTemplate.pdf b/Resources/Images/AddBranchTemplate.pdf new file mode 100644 index 000000000..925a7ae95 Binary files /dev/null and b/Resources/Images/AddBranchTemplate.pdf differ diff --git a/Resources/Images/AddLabelTemplate.pdf b/Resources/Images/AddLabelTemplate.pdf new file mode 100644 index 000000000..3ffbe770b Binary files /dev/null and b/Resources/Images/AddLabelTemplate.pdf differ diff --git a/Resources/Images/AddRemoteTemplate.pdf b/Resources/Images/AddRemoteTemplate.pdf new file mode 100644 index 000000000..05984b9e8 Binary files /dev/null and b/Resources/Images/AddRemoteTemplate.pdf differ diff --git a/Resources/Images/BranchTemplate.pdf b/Resources/Images/BranchTemplate.pdf new file mode 100644 index 000000000..74ab17d36 Binary files /dev/null and b/Resources/Images/BranchTemplate.pdf differ diff --git a/Resources/Images/Buttons.svg b/Resources/Images/Buttons.svg new file mode 100644 index 000000000..e32e401e5 --- /dev/null +++ b/Resources/Images/Buttons.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Images/CherryPickTemplate.pdf b/Resources/Images/CherryPickTemplate.pdf new file mode 100644 index 000000000..afdeda4f7 Binary files /dev/null and b/Resources/Images/CherryPickTemplate.pdf differ diff --git a/Resources/Images/DetailViewTemplate.pdf b/Resources/Images/DetailViewTemplate.pdf new file mode 100644 index 000000000..91b166e6e Binary files /dev/null and b/Resources/Images/DetailViewTemplate.pdf differ diff --git a/Resources/Images/FetchTemplate.pdf b/Resources/Images/FetchTemplate.pdf new file mode 100644 index 000000000..d42661473 Binary files /dev/null and b/Resources/Images/FetchTemplate.pdf differ diff --git a/Resources/Images/Files.svg b/Resources/Images/Files.svg new file mode 100644 index 000000000..e89ec6d50 --- /dev/null +++ b/Resources/Images/Files.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Images/FolderClosedTemplate.pdf b/Resources/Images/FolderClosedTemplate.pdf new file mode 100644 index 000000000..37b03c95a Binary files /dev/null and b/Resources/Images/FolderClosedTemplate.pdf differ diff --git a/Resources/Images/FolderTemplate.pdf b/Resources/Images/FolderTemplate.pdf new file mode 100644 index 000000000..805ee6eb4 Binary files /dev/null and b/Resources/Images/FolderTemplate.pdf differ diff --git a/Resources/Images/Images.svg b/Resources/Images/Images.svg new file mode 100644 index 000000000..aeeb29f7b --- /dev/null +++ b/Resources/Images/Images.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Images/MergeTemplate.pdf b/Resources/Images/MergeTemplate.pdf new file mode 100644 index 000000000..704c4b967 Binary files /dev/null and b/Resources/Images/MergeTemplate.pdf differ diff --git a/Resources/Images/PullTemplate.pdf b/Resources/Images/PullTemplate.pdf new file mode 100644 index 000000000..bfe9aed08 Binary files /dev/null and b/Resources/Images/PullTemplate.pdf differ diff --git a/Resources/Images/PushTemplate.pdf b/Resources/Images/PushTemplate.pdf new file mode 100644 index 000000000..b02bc3e31 Binary files /dev/null and b/Resources/Images/PushTemplate.pdf differ diff --git a/Resources/Images/RebaseTemplate.pdf b/Resources/Images/RebaseTemplate.pdf new file mode 100644 index 000000000..d58dc0cc9 Binary files /dev/null and b/Resources/Images/RebaseTemplate.pdf differ diff --git a/Resources/Images/RemoteBranchTemplate.pdf b/Resources/Images/RemoteBranchTemplate.pdf new file mode 100644 index 000000000..086c18292 Binary files /dev/null and b/Resources/Images/RemoteBranchTemplate.pdf differ diff --git a/Resources/Images/RemoteTemplate.pdf b/Resources/Images/RemoteTemplate.pdf new file mode 100644 index 000000000..8bd92ddff Binary files /dev/null and b/Resources/Images/RemoteTemplate.pdf differ diff --git a/Resources/Images/StageTemplate.pdf b/Resources/Images/StageTemplate.pdf new file mode 100644 index 000000000..4bb24aac1 Binary files /dev/null and b/Resources/Images/StageTemplate.pdf differ diff --git a/Resources/Images/TagTemplate.pdf b/Resources/Images/TagTemplate.pdf new file mode 100644 index 000000000..d55aef2a9 Binary files /dev/null and b/Resources/Images/TagTemplate.pdf differ diff --git a/Resources/Images/TreeViewTemplate.pdf b/Resources/Images/TreeViewTemplate.pdf new file mode 100644 index 000000000..27a24f1e7 Binary files /dev/null and b/Resources/Images/TreeViewTemplate.pdf differ diff --git a/Resources/Images/deleted_file.pdf b/Resources/Images/deleted_file.pdf new file mode 100644 index 000000000..9947eda76 Binary files /dev/null and b/Resources/Images/deleted_file.pdf differ diff --git a/Resources/Images/empty_file.pdf b/Resources/Images/empty_file.pdf new file mode 100644 index 000000000..c89872214 Binary files /dev/null and b/Resources/Images/empty_file.pdf differ diff --git a/Resources/Images/gitx-16.svg b/Resources/Images/gitx-16.svg new file mode 100644 index 000000000..7c8d1dc04 --- /dev/null +++ b/Resources/Images/gitx-16.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Images/gitx-32.svg b/Resources/Images/gitx-32.svg new file mode 100644 index 000000000..3f71ab416 --- /dev/null +++ b/Resources/Images/gitx-32.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Images/gitx.html b/Resources/Images/gitx.html new file mode 100644 index 000000000..543ea6be9 --- /dev/null +++ b/Resources/Images/gitx.html @@ -0,0 +1,253 @@ + + +

16

+ +

32

+ + diff --git a/Images/mainSplitterBar.tiff b/Resources/Images/mainSplitterBar.tiff similarity index 100% rename from Images/mainSplitterBar.tiff rename to Resources/Images/mainSplitterBar.tiff diff --git a/Images/mainSplitterDimple.tiff b/Resources/Images/mainSplitterDimple.tiff similarity index 100% rename from Images/mainSplitterDimple.tiff rename to Resources/Images/mainSplitterDimple.tiff diff --git a/Resources/Images/new_file.pdf b/Resources/Images/new_file.pdf new file mode 100644 index 000000000..ba2b35e6b Binary files /dev/null and b/Resources/Images/new_file.pdf differ diff --git a/Resources/Images/rewindImage.pdf b/Resources/Images/rewindImage.pdf new file mode 100644 index 000000000..40066caad Binary files /dev/null and b/Resources/Images/rewindImage.pdf differ diff --git a/SpeedTest-Info.plist b/Resources/Info-gitx.plist similarity index 67% rename from SpeedTest-Info.plist rename to Resources/Info-gitx.plist index 789bf7877..a2f74db73 100644 --- a/SpeedTest-Info.plist +++ b/Resources/Info-gitx.plist @@ -7,16 +7,16 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.yourcompany.${PRODUCT_NAME:identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleName + ${PRODUCT_NAME} CFBundlePackageType APPL - CFBundleSignature - ???? + CFBundleShortVersionString + ${CURRENT_PROJECT_VERSION} CFBundleVersion - 1.0 - NSMainNibFile - MainWindow + ${CURRENT_PROJECT_VERSION} diff --git a/Info.plist b/Resources/Info.plist similarity index 80% rename from Info.plist rename to Resources/Info.plist index 8aa62a7cf..d583dd1c7 100644 --- a/Info.plist +++ b/Resources/Info.plist @@ -2,10 +2,10 @@ + NSAppleEventsUsageDescription + Enables “Terminal Here” functionality CFBundleDevelopmentRegion English - CFBundleName - ${PRODUCT_NAME} CFBundleDocumentTypes @@ -33,33 +33,33 @@ LSTypeIsPackage NSDocumentClass - PBGitRepository + PBGitRepositoryDocument CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile - gitx.icns + AppIcon-gitx.icns CFBundleIdentifier - nl.frim.GitX + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleName + ${PRODUCT_NAME} CFBundlePackageType APPL - CFBundleSignature - ???? - CFBundleVersion - LONG_VERSION - CFBundleGitVersion - GIT_VERSION CFBundleShortVersionString - SHORT_VERSION + 1.0.3 + CFBundleVersion + 3 + LSApplicationCategoryType + public.app-category.developer-tools + NSAppleScriptEnabled + NSMainNibFile MainMenu NSPrincipalClass NSApplication - SUFeedURL - http://gitx.frim.nl/Downloads/appcast.xml NSServices @@ -68,6 +68,13 @@ default & + NSMenuItem + + default + Complete SHA1 + + NSMessage + completeSha NSPortName GitX NSReturnTypes @@ -78,14 +85,9 @@ NSStringPboardType - NSMessage - completeSha - NSMenuItem - - default - Complete SHA1 - + OSAScriptingDefinition + GitX.sdef diff --git a/Resources/UpdateKey.pem b/Resources/UpdateKey.pem new file mode 100644 index 000000000..8000392d2 --- /dev/null +++ b/Resources/UpdateKey.pem @@ -0,0 +1,20 @@ +-----BEGIN PUBLIC KEY----- +MIIDOzCCAi0GByqGSM44BAEwggIgAoIBAQDLgrI0GgvtC1rRi6dRNW+N1hcieyZZ +YFAjvnMpgUP6tsvc1j1V4wgK6pyuU1MCpbrMhSNccqO9D/Xx1ySpbUajn6WKeX8J +FXDCi1mbq7D8jUbXhReDRcI1NbI6r2cw0Rv5wQU+RFKGwaBpyNZy434k0/zTEMlY +3PzhGe3jeNOutnqc71f3LTk2wrqlVXsI4y0dl7a2JcwZyXv+/5QniKlukZIFjR5b +tiwhicOtI8AsabNM1dIAVlsady+P/DwpRsTfCRY+Mo//4RU0hSE+i8Z3y0pz71Wv +L89yS1GONRQMeYbRDX1ZZcfIYlMmVEcsHh94i199pUmkgdw08iTyjnlrAhUA+VTd +fA3w0Yptf3eWqSZPBGCayaUCggEAFhMtxRAWnHvZW3MZTfmeDs8IhhkbSfoBtD+r +2T+EUUCyYPiN4PvmF+4aNWMoWh2lsd6cpvtg5I2qo/qX1HGtQwOc2FHnMmLkUPJz +kLz7nwpzgdFQ3EyZ3jzmzvNwtsYJ7y7LzkIR22jnLslSl8SiMI+gIwze/oSTEb1X +o5xadOsFNiAANUSEa1/42xQEHbwfYWMJIEXTBx6GyKJDcep1bK4cFx0A+xnghIhm +4oah6kYG5ns7wDEcJ/+1FcZbogif5qcTjB2UFvL77jKuqRBcI8xiALVlnAPGkC3R +rT033W13tnJolae7dLy4UA3taELH+CoaoSgOk6o20ZgjJUbd0wOCAQYAAoIBAQCA +E4nDRoShNyE20aUxBC8uF5MhDJE0VomcOKPmyDd3Jl4DlEogzMGAwAZKrQKcBlkB +CAYaWes7ULDDA0RNABWHSI9t/brrQcOPrvRMA6wgcuA5oQcm8zVziuwXJwb+NqwG +so2ysvryjlUDtHPc2jJ5hUSqkp22NeQv2iK4RoQ9pDJp5/alzH7fYgqECB45AuhA +34BCKE7B2fakJ5YLQdXZOzG5O3r3azWAewqp4J34Npiqn5LSdl8FZIhMaxmi9mBa +65w0CNY4/VyyvOgPqCo5zPNxCJxypsvI0IUYRWeJFkEpQLmZ0K1bpmGT1iGnT4A5 +5llf69yaJyoGJDoD/Rj6 +-----END PUBLIC KEY----- diff --git a/Resources/XIBs/OpenRecentPopup.xib b/Resources/XIBs/OpenRecentPopup.xib new file mode 100644 index 000000000..c2d34311d --- /dev/null +++ b/Resources/XIBs/OpenRecentPopup.xib @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/XIBs/PBCommitHookFailedSheet.xib b/Resources/XIBs/PBCommitHookFailedSheet.xib new file mode 100644 index 000000000..d20bec590 --- /dev/null +++ b/Resources/XIBs/PBCommitHookFailedSheet.xib @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/XIBs/PBDiffWindow.xib b/Resources/XIBs/PBDiffWindow.xib new file mode 100644 index 000000000..9b90f84c7 --- /dev/null +++ b/Resources/XIBs/PBDiffWindow.xib @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/XIBs/PBGitCommitView.xib b/Resources/XIBs/PBGitCommitView.xib new file mode 100644 index 000000000..beb5b6acc --- /dev/null +++ b/Resources/XIBs/PBGitCommitView.xib @@ -0,0 +1,406 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + path + icon + + + + + + + + value + description + path + icon + commitBlobSHA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/XIBs/PBGitHistoryView.xib b/Resources/XIBs/PBGitHistoryView.xib new file mode 100644 index 000000000..eb8b4c0af --- /dev/null +++ b/Resources/XIBs/PBGitHistoryView.xib @@ -0,0 +1,501 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + path + contents + selectedCommitDetailsIndex + textContents + + + + + + + + self + OID + details + subject + @count + self.@count + author + children + tree + tree.children + selection.tree.children + treeCon + treeContents + treeChildren + tree.s + commits.@max.tree.children + authorDate + date + dateString + arran + SHA + relativeDateString + shortName + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/XIBs/PBGitSidebarView.xib b/Resources/XIBs/PBGitSidebarView.xib new file mode 100644 index 000000000..41119ccfd --- /dev/null +++ b/Resources/XIBs/PBGitSidebarView.xib @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/XIBs/PBGitXMessageSheet.xib b/Resources/XIBs/PBGitXMessageSheet.xib new file mode 100644 index 000000000..2c5031721 --- /dev/null +++ b/Resources/XIBs/PBGitXMessageSheet.xib @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/html/convert.playground/Contents.swift b/Resources/html/convert.playground/Contents.swift new file mode 100644 index 000000000..bfe242a3e --- /dev/null +++ b/Resources/html/convert.playground/Contents.swift @@ -0,0 +1,52 @@ +//#!/usr/bin/swift + +import Foundation + +////////////////////////////////// library + +extension String { + var isDirectory: Bool { + var isDir: ObjCBool = false + return FileManager.default.fileExists(atPath: self, isDirectory: &isDir) && isDir.boolValue + } +} + +func /(lhs: String, rhs: String) -> String { + return (lhs as NSString).appendingPathComponent(rhs) +} + +func invert(c: Character) -> Character { + let value = Int(String(c), radix: 16)! + let inverted = 15 - value + let cc = String.init(inverted, radix: 16, uppercase: true) + return cc.first! +} + +func mangle(path: String) throws { + print(path) + var html = try String(contentsOfFile: path) + let regex = try NSRegularExpression(pattern: "#[a-fA-F0-9]+") + let matches = regex.matches(in: html, options: [], range: NSRange(location: 0, length: html.count)) + for match in matches { + guard (4...7).contains(match.range.length) else { continue } + let range = Range(match.range, in: html)! + let color = html[range].dropFirst() + let inverted = String(color.map(invert)) + html.replaceSubrange(range, with: "#\(inverted)") + print(color, inverted) + } + html.replacingOccurrences(of: "color: white", with: "color: black") + html.replacingOccurrences(of: "background-color: white", with: "background-color: black") + try html.write(toFile: path, atomically: true, encoding: .utf8) +} + + +////////////////////////////////// main +let fm = FileManager.default +let path = fm.currentDirectoryPath == "/" ? "/Users/mxcl/src/GitX/Resources/html" : "." +let enumerator = fm.enumerator(atPath: path)! + +while let node = enumerator.nextObject() as? String { + guard node.hasSuffix(".css") else { continue } + try mangle(path: path/node) +} diff --git a/Resources/html/convert.playground/contents.xcplayground b/Resources/html/convert.playground/contents.xcplayground new file mode 100644 index 000000000..a93d4844a --- /dev/null +++ b/Resources/html/convert.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Resources/html/convert.playground/playground.xcworkspace/contents.xcworkspacedata b/Resources/html/convert.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Resources/html/convert.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Resources/html/convert.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Resources/html/convert.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Resources/html/convert.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Resources/html/css/GitX.css b/Resources/html/css/GitX.css new file mode 100644 index 000000000..f340f35e4 --- /dev/null +++ b/Resources/html/css/GitX.css @@ -0,0 +1,42 @@ +@import url("diff.css"); +@import url("notification.css"); + +body { + margin: 0; + margin-top: 5px; + width: 100%; + font-family: -apple-system, 'Helvetica Neue', 'Lucida Grande', sans-serif; + font-size: 12px; +} + +table { + font-size: 12px; +} + +.fileHeader a:before { + margin-left: 0.3em; + width: 1em; + display: inline-block; +} + +.expanded a:before { + content: "\25be\0020"; +} + +.collapsed a:before { + content: "\25b8\0020"; +} + +.SHA { + font-family: "SF Mono", "Menlo", Monaco, monospace; + text-decoration: none; +} + +.hidden { + display: none; +} + +html { + background: black; + color: white; +} diff --git a/Resources/html/css/diff.css b/Resources/html/css/diff.css new file mode 100644 index 000000000..0a4df574f --- /dev/null +++ b/Resources/html/css/diff.css @@ -0,0 +1,80 @@ +.diff .file { + margin: 11px; + border: 1px solid #333; +} + +.diff .file .fileHeader { + margin: 5px; + font-weight: bold; +} + +.diff .file .fileHeader a { + color: #FFF; + text-decoration: none; +} + +.diff .file .diffContent { + white-space: pre; + font-family: "SF Mono", "Menlo", Monaco, monospace; +} + +.diff .file .diffcontent .lineno { + float: left; + padding-left: 3px; + padding-right: 3px; + background-color: #131313; + color: #565656; + border-right: 1px solid #222222; + text-align: right; +} + +.diff .file .diffContent .lines { + overflow: auto; +} + +.diff .file .diffContent .lines .hunkheader { + background-color: #080808; + color: #444; +} + +.diff .file .diffContent .lines > div { + padding-left: 2px; +} + +.diff .file .diffcontent .lines .delline { + background-color: #011; + color: #4FF; +} + +.diff .file .diffcontent .lines .addline { + background-color: #202; + color: #F7F; +} + +.diff .file .diffcontent .lines .markerline { + display:none; +} + +.diff .file .diffcontent .lines .nonewline:after { + content: " \20e0\23ce"; + font-family: -apple-system, "Helvetica Neue", sans-serif; +} +.diff .file .diffcontent .lines .whitespace { + background-color: rgba(0,255,255,0.5); +} +.diff .file .diffcontent .lines del, +.diff .file .diffcontent .lines ins { + text-decoration: none; border-radius: 0.2em; +} +.diff .file .diffcontent .lines .delline del { + background-color: #014040; + color: #5FF; +} +.diff .file .diffcontent .lines .addline ins { + background-color: #4D0050; + color: #F8F; +} + +#CurrentHunk { + border-left: 5px solid black; +} diff --git a/html/css/notification.css b/Resources/html/css/notification.css similarity index 59% rename from html/css/notification.css rename to Resources/html/css/notification.css index 3d9c66097..adbcf5c4e 100644 --- a/html/css/notification.css +++ b/Resources/html/css/notification.css @@ -4,7 +4,7 @@ clear: both; border: 1px solid black; - background-color: #f3f3f3; + background-color: #0C0C0C; text-align: center; font-size: 80%; @@ -13,11 +13,11 @@ } #notification.success { - background-color: #CCFF99; - border: 1px solid #99CC66; + background-color: #330066; + border: 1px solid #663399; } #notification.fail { - background-color: #ff9999; - border: 1px solid #cc6666; + background-color: #006666; + border: 1px solid #339999; } \ No newline at end of file diff --git a/Resources/html/css/shCoreGitX.css b/Resources/html/css/shCoreGitX.css new file mode 100644 index 000000000..1a44decee --- /dev/null +++ b/Resources/html/css/shCoreGitX.css @@ -0,0 +1,225 @@ +/** + * SyntaxHighlighter + * http://alexgorbatchev.com/SyntaxHighlighter + * + * SyntaxHighlighter is donationware. If you are using it, please donate. + * http://alexgorbatchev.com/SyntaxHighlighter/donate.html + * + * @version + * 3.0.83 (July 02 2010) + * + * @copyright + * Copyright (C) 2004-2010 Alex Gorbatchev. + * + * @license + * Dual licensed under the MIT and GPL licenses. + */ +.syntaxhighlighter a, +.syntaxhighlighter div, +.syntaxhighlighter code, +.syntaxhighlighter table, +.syntaxhighlighter table td, +.syntaxhighlighter table tr, +.syntaxhighlighter table tbody, +.syntaxhighlighter table thead, +.syntaxhighlighter table caption, +.syntaxhighlighter textarea { + -moz-border-radius: 0 0 0 0 !important; + -webkit-border-radius: 0 0 0 0 !important; + background: none !important; + border: 0 !important; + bottom: auto !important; + float: none !important; + height: auto !important; + left: auto !important; + line-height: 1.1em !important; + margin: 0 !important; + outline: 0 !important; + overflow: visible !important; + padding: 0 !important; + position: static !important; + right: auto !important; + text-align: left !important; + top: auto !important; + vertical-align: baseline !important; + width: auto !important; + box-sizing: content-box !important; + font-family: "Menlo" , "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; + font-weight: normal !important; + font-style: normal !important; + font-size: 1em !important; + min-height: inherit !important; + min-height: auto !important; +} + +.syntaxhighlighter { + width: 100% !important; + margin: 0 !important; + position: relative !important; + font-size: 1em !important; +} +.syntaxhighlighter.source { + overflow: hidden !important; +} +.syntaxhighlighter .bold { + font-weight: bold !important; +} +.syntaxhighlighter .italic { + font-style: italic !important; +} +.syntaxhighlighter .line { + white-space: pre !important; +} +.syntaxhighlighter table { + width: 100% !important; +} +.syntaxhighlighter table caption { + text-align: left !important; + padding: .5em 0 0.5em 1em !important; +} +.syntaxhighlighter table td.code { + width: 100% !important; +} +.syntaxhighlighter table td.code .container { + position: relative !important; +} +.syntaxhighlighter table td.code .container textarea { + box-sizing: border-box !important; + position: absolute !important; + left: 0 !important; + top: 0 !important; + width: 100% !important; + height: 100% !important; + border: none !important; + background: white !important; + padding-left: 1em !important; + overflow: hidden !important; + white-space: pre !important; +} +.syntaxhighlighter table td.gutter .line { + text-align: right !important; + padding: 0 0.5em 0 1em !important; +} +.syntaxhighlighter table td.code .line { + padding: 0 1em !important; +} +.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { + padding-left: 0em !important; +} +.syntaxhighlighter.show { + display: block !important; +} +.syntaxhighlighter.collapsed table { + display: none !important; +} +.syntaxhighlighter.collapsed .toolbar { + padding: 0.1em 0.8em 0em 0.8em !important; + font-size: 1em !important; + position: static !important; + width: auto !important; + height: auto !important; +} +.syntaxhighlighter.collapsed .toolbar span { + display: inline !important; + margin-right: 1em !important; +} +.syntaxhighlighter.collapsed .toolbar span a { + padding: 0 !important; + display: none !important; +} +.syntaxhighlighter.collapsed .toolbar span a.expandSource { + display: inline !important; +} +.syntaxhighlighter .toolbar { + position: absolute !important; + right: 1px !important; + top: 1px !important; + width: 11px !important; + height: 11px !important; + font-size: 10px !important; + z-index: 10 !important; +} +.syntaxhighlighter .toolbar span.title { + display: inline !important; +} +.syntaxhighlighter .toolbar a { + display: block !important; + text-align: center !important; + text-decoration: none !important; + padding-top: 1px !important; +} +.syntaxhighlighter .toolbar a.expandSource { + display: none !important; +} +.syntaxhighlighter.ie { + font-size: .9em !important; + padding: 1px 0 1px 0 !important; +} +.syntaxhighlighter.ie .toolbar { + line-height: 8px !important; +} +.syntaxhighlighter.ie .toolbar a { + padding-top: 0px !important; +} +.syntaxhighlighter.printing .line.alt1 .content, +.syntaxhighlighter.printing .line.alt2 .content, +.syntaxhighlighter.printing .line.highlighted .number, +.syntaxhighlighter.printing .line.highlighted.alt1 .content, +.syntaxhighlighter.printing .line.highlighted.alt2 .content { + background: none !important; +} +.syntaxhighlighter.printing .line .number { + color: #444444 !important; +} +.syntaxhighlighter.printing .line .content { + color: black !important; +} +.syntaxhighlighter.printing .toolbar { + display: none !important; +} +.syntaxhighlighter.printing a { + text-decoration: none !important; +} +.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { + color: black !important; +} +.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { + color: #FF7DFF !important; +} +.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { + color: blue !important; +} +.syntaxhighlighter.printing .keyword { + color: #FF9966 !important; + font-weight: bold !important; +} +.syntaxhighlighter.printing .preprocessor { + color: gray !important; +} +.syntaxhighlighter.printing .variable { + color: #5588FF !important; +} +.syntaxhighlighter.printing .value { + color: #FF66FF !important; +} +.syntaxhighlighter.printing .functions { + color: #00EB6C !important; +} +.syntaxhighlighter.printing .constants { + color: #FF9933 !important; +} +.syntaxhighlighter.printing .script { + font-weight: bold !important; +} +.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { + color: gray !important; +} +.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { + color: #00EB6C !important; +} +.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { + color: red !important; +} +.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { + color: black !important; +} diff --git a/Resources/html/css/shThemeGitX.css b/Resources/html/css/shThemeGitX.css new file mode 100644 index 000000000..1cfac0251 --- /dev/null +++ b/Resources/html/css/shThemeGitX.css @@ -0,0 +1,126 @@ +/** + * SyntaxHighlighter + * http://alexgorbatchev.com/SyntaxHighlighter + * + * SyntaxHighlighter is donationware. If you are using it, please donate. + * http://alexgorbatchev.com/SyntaxHighlighter/donate.html + * + * @version + * 3.0.83 (July 02 2010) + * + * @copyright + * Copyright (C) 2004-2010 Alex Gorbatchev. + * + * @license + * Dual licensed under the MIT and GPL licenses. + */ +.syntaxhighlighter { + background-color: white !important; + font: 1em "Menlo" !important; +} +.syntaxhighlighter .line.alt1 { + background-color: white !important; +} +.syntaxhighlighter .line.alt2 { + background-color: white !important; +} +.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { + background-color: #1F1F1F !important; +} +.syntaxhighlighter .line.highlighted.number { + color: black !important; +} +.syntaxhighlighter table caption { + color: black !important; +} +.syntaxhighlighter .gutter { + border: 1px solid #222222 !important; + padding: 5px 0 !important; + background-color: #131313 !important; +} +.syntaxhighlighter table td.gutter .line { + text-align: right !important; + padding: 0 2px 0 2px !important; + min-width: 20px; +} +.syntaxhighlighter .gutter .line { + background-color: #131313 !important; + color: #565656 !important; +} +.syntaxhighlighter .gutter .line.highlighted { + background-color: #931D93 !important; + color: white !important; +} +.syntaxhighlighter.printing .line .content { + border: none !important; +} +.syntaxhighlighter.collapsed { + overflow: visible !important; +} +.syntaxhighlighter.collapsed .toolbar { + color: blue !important; + background: white !important; + border: 1px solid #931D93 !important; +} +.syntaxhighlighter.collapsed .toolbar a { + color: blue !important; +} +.syntaxhighlighter.collapsed .toolbar a:hover { + color: red !important; +} +.syntaxhighlighter .toolbar { + color: white !important; + background: #931D93 !important; + border: none !important; +} +.syntaxhighlighter .toolbar a { + color: white !important; +} +.syntaxhighlighter .toolbar a:hover { + color: black !important; +} +.syntaxhighlighter .plain, .syntaxhighlighter .plain a { + color: black !important; +} +.syntaxhighlighter .comments, .syntaxhighlighter .comments a { + color: #FF7DFF !important; +} +.syntaxhighlighter .string, .syntaxhighlighter .string a { + color: blue !important; +} +.syntaxhighlighter .keyword { + color: #FF9966 !important; +} +.syntaxhighlighter .preprocessor { + color: gray !important; +} +.syntaxhighlighter .variable { + color: #5588FF !important; +} +.syntaxhighlighter .value { + color: #FF66FF !important; +} +.syntaxhighlighter .functions { + color: #00EB6C !important; +} +.syntaxhighlighter .constants { + color: #FF9933 !important; +} +.syntaxhighlighter .script { + font-weight: bold !important; + color: #FF9966 !important; + background-color: none !important; +} +.syntaxhighlighter .color1, .syntaxhighlighter .color1 a { + color: gray !important; +} +.syntaxhighlighter .color2, .syntaxhighlighter .color2 a { + color: #00EB6C !important; +} +.syntaxhighlighter .color3, .syntaxhighlighter .color3 a { + color: red !important; +} + +.syntaxhighlighter .keyword { + font-weight: bold !important; +} diff --git a/html/dragtest.html b/Resources/html/dragtest.html similarity index 98% rename from html/dragtest.html rename to Resources/html/dragtest.html index dfa4c5c50..c9214655a 100644 --- a/html/dragtest.html +++ b/Resources/html/dragtest.html @@ -3,6 +3,7 @@ + diff --git a/Resources/html/images/added.svg b/Resources/html/images/added.svg new file mode 100644 index 000000000..7d3a09d92 --- /dev/null +++ b/Resources/html/images/added.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/html/images/modified.svg b/Resources/html/images/modified.svg new file mode 100644 index 000000000..ff43ee5aa --- /dev/null +++ b/Resources/html/images/modified.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/html/images/removed.svg b/Resources/html/images/removed.svg new file mode 100644 index 000000000..6da7ce4b9 --- /dev/null +++ b/Resources/html/images/removed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/html/images/renamed.svg b/Resources/html/images/renamed.svg new file mode 100644 index 000000000..777fafbbb --- /dev/null +++ b/Resources/html/images/renamed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/html/images/spinner.gif b/Resources/html/images/spinner.gif similarity index 100% rename from html/images/spinner.gif rename to Resources/html/images/spinner.gif diff --git a/Resources/html/inlinedifftest.html b/Resources/html/inlinedifftest.html new file mode 100644 index 000000000..2b2846e4a --- /dev/null +++ b/Resources/html/inlinedifftest.html @@ -0,0 +1,132 @@ + + + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/Resources/html/lib/GitX.js b/Resources/html/lib/GitX.js new file mode 100644 index 000000000..3347a652b --- /dev/null +++ b/Resources/html/lib/GitX.js @@ -0,0 +1,68 @@ +/* + * GitX Javascript library + * This library contains functions that can be shared across all + * webviews in GitX. + * It is written only for Safari 3 and higher. + */ + +function $(element) { + return document.getElementById(element); +} + +String.prototype.escapeHTML = (function() { + var div = document.createElement("div"); + return function() { + div.textContent = this; + return div.innerHTML; + }; +})(); + +Element.prototype.toggleDisplay = function() { + if (this.style.display != "") + this.style.display = ""; + else + this.style.display = "none"; +} + +Array.prototype.indexOf = function(item, i) { + i || (i = 0); + var length = this.length; + if (i < 0) i = length + i; + for (; i < length; i++) + if (this[i] === item) return i; + return -1; +}; + +var notify = function(html, state) { + var n = $("notification"); + n.classList.remove("hidden"); + $("notification_message").innerHTML = html; + + // Change color + if (!state) { // Busy + $("spinner").classList.remove("hidden"); + n.classList.remove("success"); + n.classList.remove("fail"); + } + else if (state == 1) { // Success + $("spinner").classList.add("hidden"); + n.classList.add("success"); + } else if (state == -1) {// Fail + $("spinner").classList.add("hidden"); + n.classList.add("fail"); + } +} + +var hideNotification = function() { + $("notification").classList.add("hidden"); +} + +var bindCommitSelectionLinks = function(el) { + var links = el.getElementsByClassName("commit-link"); + for (var i = 0, n = links.length; i < n; ++i) { + links[i].addEventListener("click", function(e) { + e.preventDefault(); + selectCommit(this.dataset.commitId || this.innerHTML); + }, false); + } +}; diff --git a/Resources/html/lib/diffHighlighter.js b/Resources/html/lib/diffHighlighter.js new file mode 100644 index 000000000..d2b40eaf4 --- /dev/null +++ b/Resources/html/lib/diffHighlighter.js @@ -0,0 +1,527 @@ +// If we run from a Safari instance, we don't +// have a Controller object. Instead, we fake it by +// using the console +if (typeof Controller == 'undefined') { + Controller = console; + Controller.log_ = console.log; +} + +var toggleDiff = function(id) +{ + var content = document.getElementById('content_' + id); + if (content) { + var collapsed = (content.style.display == 'none'); + if (collapsed) { + content.style.display = 'box'; + jQuery(content).slideDown(100); + } else { + jQuery(content).slideUp(100, function () {content.style.display = 'none'}); + } + + var title = document.getElementById('title_' + id); + if (title) { + if (collapsed) { + title.classList.remove('collapsed'); + title.classList.add('expanded'); + } + else { + title.classList.add('collapsed'); + title.classList.remove('expanded'); + } + } + } +} + +var highlightDiff = function(diff, element, callbacks) { + if (!diff || diff == "") + return; + + if (!callbacks) + callbacks = {}; + var start = new Date().getTime(); + element.className = "diff" + var content = diff.escapeHTML(); + + var file_index = 0; + + var startname = ""; + var endname = ""; + var line1 = ""; + var line2 = ""; + var diffContent = ""; + var finalContent = ""; + var lines = content.split('\n'); + var binary = false; + var mode_change = false; + var old_mode = ""; + var new_mode = ""; + var linkToTop = ""; + + var hunk_start_line_1 = -1; + var hunk_start_line_2 = -1; + + var header = false; + + var finishContent = function() + { + if (!file_index) + { + file_index++; + return; + } + + if (callbacks["newfile"]) + callbacks["newfile"](startname, endname, "file_index_" + (file_index - 1), mode_change, old_mode, new_mode); + + var title = startname; + var binaryname = endname; + if (endname == "/dev/null") { + binaryname = startname; + title = startname; + } + else if (startname == "/dev/null") + title = endname; + else if (startname != endname) + title = startname + " renamed to " + endname; + + // Show file list header + finalContent += '
'; + finalContent += ''; + + if (binary) { + // diffContent is assumed to be empty for binary files + if (callbacks.binaryFileHTML) { + finalContent += callbacks.binaryFileHTML(binaryname); + } else { + finalContent += '
Binary file differs
'; + } + } else if (diffContent != "") { + finalContent += '
' + + '
' + line1 + '
' + + '
' + line2 + '
' + + '
' + postProcessDiffContents(diffContent).replace(/\t/g, " ") + '
' + + '
'; + } + + // Close div.file + finalContent += '
' + linkToTop; + + line1 = ""; + line2 = ""; + diffContent = ""; + file_index++; + startname = ""; + endname = ""; + } + for (var lineno = 0, lindex = 0; lineno < lines.length; lineno++) { + var l = lines[lineno]; + + var firstChar = l.charAt(0); + + if (firstChar == "d" && l.charAt(1) == "i") { + // "diff", i.e. new file, we have to reset everything + + // diff always starts with a header + header = true; + + // Finish last file + finishContent(); + + binary = false; + mode_change = false; + + // there are cases when we need to capture filenames from the diff + // line, like with mode-changes. this can get overridden later if + // there is a diff or if the file is binary + if (match = l.match(/^diff --git ([a-z]\/)+(.*) ([a-z]\/)+(.*)$/)) { + startname = match[2]; + endname = match[4]; + } + + continue; + } + + if (header) { + if (firstChar == "n") { + if (l.match(/^new file mode .*$/)) + startname = "/dev/null"; + + if (match = l.match(/^new mode (.*)$/)) { + mode_change = true; + new_mode = match[1]; + } + continue; + } + if (firstChar == "o") { + if (match = l.match(/^old mode (.*)$/)) { + mode_change = true; + old_mode = match[1]; + } + continue; + } + + if (firstChar == "d") { + if (l.match(/^deleted file mode .*$/)) + endname = "/dev/null"; + continue; + } + if (firstChar == "-") { + if (match = l.match(/^--- ([a-z]\/)?(.*)$/)) + startname = match[2]; + continue; + } + if (firstChar == "+") { + if (match = l.match(/^\+\+\+ ([a-z]\/)?(.*)$/)) + endname = match[2]; + continue; + } + // If it is a complete rename, we don't know the name yet + // We can figure this out from the 'rename from.. rename to.. thing + if (firstChar == 'r') + { + if (match = l.match(/^rename (from|to) (.*)$/)) + { + if (match[1] == "from") + startname = match[2]; + else + endname = match[2]; + } + continue; + } + if (firstChar == "B") // "Binary files .. and .. differ" + { + binary = true; + // We might not have a diff from the binary file if it's new. + // So, we use a regex to figure that out + + if (match = l.match(/^Binary files ([a-z]\/)?(.*) and ([a-z]\/)?(.*) differ$/)) + { + startname = match[2]; + endname = match[4]; + } + } + + // Finish the header + if (firstChar == "@") + header = false; + else + continue; + } + + var isMissingEOFNewline = + lineno + 1 < lines.length && + lines[lineno + 1] == "\\ No newline at end of file"; + var missingEOFNewlineClass = isMissingEOFNewline ? " nonewline" : ""; + var wrapLine = function(cls) { + return "
" + + l + "
"; + }; + + sindex = "index=" + lindex.toString() + " "; + if (firstChar == "+") { + line1 += "\n"; + line2 += ++hunk_start_line_2 + "\n"; + diffContent += wrapLine("addline"); + } else if (firstChar == "-") { + line1 += ++hunk_start_line_1 + "\n"; + line2 += "\n"; + diffContent += wrapLine("delline"); + } else if (firstChar == "@") { + if (header) { + header = false; + } + + if (m = l.match(/@@ \-([0-9]+),?\d* \+(\d+),?\d* @@/)) + { + hunk_start_line_1 = parseInt(m[1]) - 1; + hunk_start_line_2 = parseInt(m[2]) - 1; + } + line1 += "...\n"; + line2 += "...\n"; + diffContent += wrapLine("hunkheader"); + } else if (firstChar == " ") { + line1 += ++hunk_start_line_1 + "\n"; + line2 += ++hunk_start_line_2 + "\n"; + diffContent += wrapLine("noopline"); + } else if (firstChar == "\\") { + diffContent += wrapLine("markerline"); + } + lindex++; + } + + finishContent(); + + // This takes about 7ms + element.innerHTML = finalContent; + + var fileHeaders = element.querySelectorAll(".fileHeader a"); + for (var i = 0, n = fileHeaders.length; i < n; ++i) { + fileHeaders[i].addEventListener("click", function(e) { + e.preventDefault(); + toggleDiff(this.innerHTML); + }); + } + if (callbacks.binaryFileClass && callbacks.binaryFileOnClick) { + var binaryFiles = element.getElementsByClassName(callbacks.binaryFileClass); + for (var i = 0, n = binaryFiles.length; i < n; ++i) { + binaryFiles[i].addEventListener("click", callbacks.binaryFileOnClick); + } + } + + // TODO: Replace this with a performance pref call + if (false) + Controller.log_("Total time:" + (new Date().getTime() - start)); +} + +var highlightTrailingWhitespace = function (l) { + // Highlight trailing whitespace + l = l.replace(/(\s+)(<\/ins>)?$/, '$1$2'); + return l; +} + +var mergeInsDel = function (html) { + return html + .replace(/^<\/(ins|del)>|<(ins|del)>$/g,'') + .replace(/<\/(ins|del)><\1>/g,''); +} + +var postProcessDiffContents = function(diffContent) { + var $ = jQuery; + var diffEl = $(diffContent); + var dumbEl = $('
'); + var newContent = ""; + var oldEls = []; + var newEls = []; + var flushBuffer = function () { + if (oldEls.length || newEls.length) { + var buffer = ""; + if (!oldEls.length || !newEls.length) { + // hunk only contains additions OR deletions, so there is no need + // to do any inline-diff. just keep the elements as they are + buffer = $.map(oldEls.length ? oldEls : newEls, function (e) { + var prefix = e.text().substring(0,1), + text = inlinediff.escape(e.text().substring(1)), + tag = prefix=='+' ? 'ins' : 'del', + html = prefix+'<'+tag+'>'+(prefix == "+" ? highlightTrailingWhitespace(text) : text)+''; + e.html(html); + return dumbEl.html(e).html(); + }).join(""); + } + else { + // hunk contains additions AND deletions. so we create an inline diff + // of all the old and new lines together and merge the result back to buffer + var mapFn = function (e) { return e.text().substring(1).replace(/\r?\n|\r/g,''); }; + var oldText = $.map(oldEls, mapFn).join("\n"); + var newText = $.map(newEls, mapFn).join("\n"); + var diffResult = inlinediff.diffString3(oldText,newText); + diffLines = (diffResult[1] + "\n" + diffResult[2]).split(/\n/g); + + buffer = $.map(oldEls, function (e, i) { + var di = i; + e.html("-"+mergeInsDel(diffLines[di])); + return dumbEl.html(e).html(); + }).join("") + $.map(newEls, function (e, i) { + var di = i + oldEls.length; + var line = mergeInsDel(highlightTrailingWhitespace(diffLines[di])); + e.html("+"+line); + return dumbEl.html(e).html(); + }).join(""); + } + newContent+= buffer; + oldEls = []; + newEls = []; + } + }; + diffEl.each(function (i, e) { + e = $(e); + var isAdd = e.is(".addline"); + var isDel = e.is(".delline"); + var text = e.text(); + var html = dumbEl.html(e).html(); + if (isAdd) { + newEls.push(e); + } + else if (isDel) { + oldEls.push(e); + } + else { + flushBuffer(); + newContent+= html; + } + }); + flushBuffer(); + return newContent; +} + + +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * Adapted for GitX by Mathias Leppich http://github.com/muhqu + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + */ + +var inlinediff = (function () { + return { + diffString: diffString, + diffString3: diffString3, + escape: escape + }; + + function escape(s) { + return s.escapeHTML(); + } + + function diffString( o, n ) { + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); + var str = ""; + + var oSpace = o.match(/\s+/g); + if (oSpace == null) { + oSpace = ["\n"]; + } else { + oSpace.push("\n"); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) { + nSpace = ["\n"]; + } else { + nSpace.push("\n"); + } + + if (out.n.length == 0) { + for (var i = 0; i < out.o.length; i++) { + str += '' + escape(out.o[i]) + oSpace[i] + ""; + } + } else { + if (out.n[0].text == null) { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) { + str += '' + escape(out.o[n]) + oSpace[n] + ""; + } + } + + for ( var i = 0; i < out.n.length; i++ ) { + if (out.n[i].text == null) { + str += '' + escape(out.n[i]) + nSpace[i] + ""; + } else { + var pre = ""; + + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { + pre += '' + escape(out.o[n]) + oSpace[n] + ""; + } + str += escape(out.n[i].text) + nSpace[i] + pre; + } + } + } + + return str; + } + + function whitespaceAwareTokenize(n) { + return n !== "" && n.match(/\n| *[\->'+escape(c)+''; + } + + function diffString3( o, n ) { + var out = diff(whitespaceAwareTokenize(o), whitespaceAwareTokenize(n)); + var ac = [], ao = [], an = []; + if (out.n.length == 0) { + for (var i = 0; i < out.o.length; i++) { + ac.push(tag('del',out.o[i])); + ao.push(tag('del',out.o[i])); + } + } else { + if (out.n[0].text == null) { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) { + ac.push(tag('del',out.o[n])); + } + } + + var added = 0; + for ( var i = 0; i < out.o.length; i++ ) { + if (out.o[i].text == null) { + ao.push(tag('del',out.o[i])); added++; + } else { + var moved = (i - out.o[i].row - added); + ao.push(tag((moved>0) ? 'del' : '',out.o[i].text)); + } + } + + var removed = 0; + for ( var i = 0; i < out.n.length; i++ ) { + if (out.n[i].text == null) { + ac.push(tag('ins',out.n[i])); + an.push(tag('ins',out.n[i])); + } else { + var moved = (i - out.n[i].row + removed); + an.push(tag((moved<0)?'ins':'', out.n[i].text)); + ac.push(escape(out.n[i].text)); + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { + ac.push(tag('del',out.o[n])); removed++; + } + } + } + } + return [ + ac.join(""), // anotated combined additions and deletions + ao.join(""), // old with highlighted deletions + an.join("") // new with highlighted additions + ]; + } + + function diff( o, n ) { + var ns = {}, os = {}, k = null, i = 0; + + for ( var i = 0; i < n.length; i++ ) { + k = '"' + n[i]; // prefix keys with a quote to not collide with Object's internal keys, e.g. '__proto__' or 'constructor' + if ( ns[k] === undefined ) + ns[k] = { rows: [], o: null }; + ns[k].rows.push( i ); + } + + for ( var i = 0; i < o.length; i++ ) { + k = '"' + o[i] + if ( os[k] === undefined ) + os[k] = { rows: [], n: null }; + os[k].rows.push( i ); + } + + for ( var k in ns ) { + if ( ns[k].rows.length == 1 && os[k] !== undefined && os[k].rows.length == 1 ) { + n[ ns[k].rows[0] ] = { text: n[ ns[k].rows[0] ], row: os[k].rows[0] }; + o[ os[k].rows[0] ] = { text: o[ os[k].rows[0] ], row: ns[k].rows[0] }; + } + } + + for ( var i = 0; i < n.length - 1; i++ ) { + if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && + n[i+1] == o[ n[i].row + 1 ] ) { + n[i+1] = { text: n[i+1], row: n[i].row + 1 }; + o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 }; + } + } + + for ( var i = n.length - 1; i > 0; i-- ) { + if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && + n[i-1] == o[ n[i].row - 1 ] ) { + n[i-1] = { text: n[i-1], row: n[i].row - 1 }; + o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 }; + } + } + + return { o: o, n: n }; + } +})(); + diff --git a/Resources/html/lib/jquery-1.3.2.min.js b/Resources/html/lib/jquery-1.3.2.min.js new file mode 100644 index 000000000..b1ae21d8b --- /dev/null +++ b/Resources/html/lib/jquery-1.3.2.min.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/Resources/html/lib/jquery-2.0.2.min.js b/Resources/html/lib/jquery-2.0.2.min.js new file mode 100644 index 000000000..73e5218d2 --- /dev/null +++ b/Resources/html/lib/jquery-2.0.2.min.js @@ -0,0 +1,6 @@ +/*! jQuery v2.0.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-2.0.2.min.map +*/ +(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],p="2.0.2",f=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:p,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return f.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,p,f,h,d,g,m,y,v="sizzle"+-new Date,b=e.document,w=0,T=0,C=at(),k=at(),N=at(),E=!1,S=function(){return 0},j=typeof undefined,D=1<<31,A={}.hasOwnProperty,L=[],H=L.pop,q=L.push,O=L.push,F=L.slice,P=L.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",W="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",$=W.replace("w","w#"),B="\\["+M+"*("+W+")"+M+"*(?:([*^$|!~]?=)"+M+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+$+")|)|)"+M+"*\\]",I=":("+W+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+B.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=RegExp("^"+M+"*,"+M+"*"),X=RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=RegExp(M+"*[+~]"),Y=RegExp("="+M+"*([^\\]'\"]*)"+M+"*\\]","g"),V=RegExp(I),G=RegExp("^"+$+"$"),J={ID:RegExp("^#("+W+")"),CLASS:RegExp("^\\.("+W+")"),TAG:RegExp("^("+W.replace("w","w*")+")"),ATTR:RegExp("^"+B),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:RegExp("^(?:"+R+")$","i"),needsContext:RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Q=/^[^{]+\{\s*\[native \w/,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Z=/^(?:input|select|textarea|button)$/i,et=/^h\d$/i,tt=/'|\\/g,nt=RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),rt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{O.apply(L=F.call(b.childNodes),b.childNodes),L[b.childNodes.length].nodeType}catch(it){O={apply:L.length?function(e,t){q.apply(e,F.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function ot(e,t,r,i){var o,s,a,u,l,f,g,m,x,w;if((t?t.ownerDocument||t:b)!==p&&c(t),t=t||p,r=r||[],!e||"string"!=typeof e)return r;if(1!==(u=t.nodeType)&&9!==u)return[];if(h&&!i){if(o=K.exec(e))if(a=o[1]){if(9===u){if(s=t.getElementById(a),!s||!s.parentNode)return r;if(s.id===a)return r.push(s),r}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(a))&&y(t,s)&&s.id===a)return r.push(s),r}else{if(o[2])return O.apply(r,t.getElementsByTagName(e)),r;if((a=o[3])&&n.getElementsByClassName&&t.getElementsByClassName)return O.apply(r,t.getElementsByClassName(a)),r}if(n.qsa&&(!d||!d.test(e))){if(m=g=v,x=t,w=9===u&&e,1===u&&"object"!==t.nodeName.toLowerCase()){f=vt(e),(g=t.getAttribute("id"))?m=g.replace(tt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",l=f.length;while(l--)f[l]=m+xt(f[l]);x=U.test(e)&&t.parentNode||t,w=f.join(",")}if(w)try{return O.apply(r,x.querySelectorAll(w)),r}catch(T){}finally{g||t.removeAttribute("id")}}}return St(e.replace(z,"$1"),t,r,i)}function st(e){return Q.test(e+"")}function at(){var e=[];function t(n,r){return e.push(n+=" ")>i.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[v]=!0,e}function lt(e){var t=p.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t,n){e=e.split("|");var r,o=e.length,s=n?null:t;while(o--)(r=i.attrHandle[e[o]])&&r!==t||(i.attrHandle[e[o]]=s)}function pt(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function ft(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:undefined}function dt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function gt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function yt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}s=ot.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},n=ot.support={},c=ot.setDocument=function(e){var t=e?e.ownerDocument||e:b,r=t.parentWindow;return t!==p&&9===t.nodeType&&t.documentElement?(p=t,f=t.documentElement,h=!s(t),r&&r.frameElement&&r.attachEvent("onbeforeunload",function(){c()}),n.attributes=lt(function(e){return e.innerHTML="",ct("type|href|height|width",ft,"#"===e.firstChild.getAttribute("href")),ct(R,pt,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),n.input=lt(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),ct("value",ht,n.attributes&&n.input),n.getElementsByTagName=lt(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=lt(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),n.getById=lt(function(e){return f.appendChild(e).id=v,!t.getElementsByName||!t.getElementsByName(v).length}),n.getById?(i.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){return e.getAttribute("id")===t}}):(delete i.find.ID,i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=n.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.CLASS=n.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&h?t.getElementsByClassName(e):undefined},g=[],d=[],(n.qsa=st(t.querySelectorAll))&&(lt(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll(":checked").length||d.push(":checked")}),lt(function(e){var n=t.createElement("input");n.setAttribute("type","hidden"),e.appendChild(n).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&d.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||d.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),d.push(",.*:")})),(n.matchesSelector=st(m=f.webkitMatchesSelector||f.mozMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&<(function(e){n.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",I)}),d=d.length&&RegExp(d.join("|")),g=g.length&&RegExp(g.join("|")),y=st(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},n.sortDetached=lt(function(e){return 1&e.compareDocumentPosition(t.createElement("div"))}),S=f.compareDocumentPosition?function(e,r){if(e===r)return E=!0,0;var i=r.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(r);return i?1&i||!n.sortDetached&&r.compareDocumentPosition(e)===i?e===t||y(b,e)?-1:r===t||y(b,r)?1:l?P.call(l,e)-P.call(l,r):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],u=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:l?P.call(l,e)-P.call(l,n):0;if(o===s)return dt(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)u.unshift(r);while(a[i]===u[i])i++;return i?dt(a[i],u[i]):a[i]===b?-1:u[i]===b?1:0},t):p},ot.matches=function(e,t){return ot(e,null,null,t)},ot.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Y,"='$1']"),!(!n.matchesSelector||!h||g&&g.test(t)||d&&d.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return ot(t,p,null,[e]).length>0},ot.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},ot.attr=function(e,t){(e.ownerDocument||e)!==p&&c(e);var r=i.attrHandle[t.toLowerCase()],o=r&&A.call(i.attrHandle,t.toLowerCase())?r(e,t,!h):undefined;return o===undefined?n.attributes||!h?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null:o},ot.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ot.uniqueSort=function(e){var t,r=[],i=0,o=0;if(E=!n.detectDuplicates,l=!n.sortStable&&e.slice(0),e.sort(S),E){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return e},o=ot.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=ot.selectors={cacheLength:50,createPseudo:ut,match:J,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(nt,rt),e[3]=(e[4]||e[5]||"").replace(nt,rt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ot.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ot.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return J.CHILD.test(e[0])?null:(e[3]&&e[4]!==undefined?e[2]=e[4]:n&&V.test(n)&&(t=vt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(nt,rt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ot.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,y=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){p=t;while(p=p[g])if(a?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[v]||(m[v]={}),l=c[e]||[],h=l[0]===w&&l[1],f=l[0]===w&&l[2],p=h&&m.childNodes[h];while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[w,h,f];break}}else if(x&&(l=(t[v]||(t[v]={}))[e])&&l[0]===w)f=l[1];else while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if((a?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(x&&((p[v]||(p[v]={}))[e]=[w,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||ot.error("unsupported pseudo: "+e);return r[v]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),s=o.length;while(s--)i=P.call(e,o[s]),e[i]=!(n[i]=o[s])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=a(e.replace(z,"$1"));return r[v]?ut(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return ot(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||ot.error("unsupported lang: "+e),e=e.replace(nt,rt).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return et.test(e.nodeName)},input:function(e){return Z.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:yt(function(){return[0]}),last:yt(function(e,t){return[t-1]}),eq:yt(function(e,t,n){return[0>n?n+t:n]}),even:yt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:yt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:yt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:yt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[t]=gt(t);for(t in{submit:!0,reset:!0})i.pseudos[t]=mt(t);function vt(e,t){var n,r,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=i.preFilter;while(a){(!n||(r=_.exec(a)))&&(r&&(a=a.slice(r[0].length)||a),u.push(o=[])),n=!1,(r=X.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(z," ")}),a=a.slice(n.length));for(s in i.filter)!(r=J[s].exec(a))||l[s]&&!(r=l[s](r))||(n=r.shift(),o.push({value:n,type:s,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ot.error(e):k(e,u).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function bt(e,t,n){var i=t.dir,o=n&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,a){var u,l,c,p=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[v]||(t[v]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,a)||r,l[1]===!0)return!0}}function wt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Tt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function Ct(e,t,n,r,i,o){return r&&!r[v]&&(r=Ct(r)),i&&!i[v]&&(i=Ct(i,o)),ut(function(o,s,a,u){var l,c,p,f=[],h=[],d=s.length,g=o||Et(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:Tt(g,f,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=Tt(y,h),r(l,[],a,u),c=l.length;while(c--)(p=l[c])&&(y[h[c]]=!(m[h[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?P.call(o,p):f[c])>-1&&(o[l]=!(s[l]=p))}}else y=Tt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):O.apply(s,y)})}function kt(e){var t,n,r,o=e.length,s=i.relative[e[0].type],a=s||i.relative[" "],l=s?1:0,c=bt(function(e){return e===t},a,!0),p=bt(function(e){return P.call(t,e)>-1},a,!0),f=[function(e,n,r){return!s&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>l;l++)if(n=i.relative[e[l].type])f=[bt(wt(f),n)];else{if(n=i.filter[e[l].type].apply(null,e[l].matches),n[v]){for(r=++l;o>r;r++)if(i.relative[e[r].type])break;return Ct(l>1&&wt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),o>r&&kt(e=e.slice(r)),o>r&&xt(e))}f.push(n)}return wt(f)}function Nt(e,t){var n=0,o=t.length>0,s=e.length>0,a=function(a,l,c,f,h){var d,g,m,y=[],v=0,x="0",b=a&&[],T=null!=h,C=u,k=a||s&&i.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(u=l!==p&&l,r=n);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,c)){f.push(d);break}T&&(w=N,r=++n)}o&&((d=!m&&d)&&v--,a&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,c);if(a){if(v>0)while(x--)b[x]||y[x]||(y[x]=H.call(f));y=Tt(y)}O.apply(f,y),T&&!a&&y.length>0&&v+t.length>1&&ot.uniqueSort(f)}return T&&(w=N,u=C),b};return o?ut(a):a}a=ot.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=vt(e)),n=t.length;while(n--)o=kt(t[n]),o[v]?r.push(o):i.push(o);o=N(e,Nt(i,r))}return o};function Et(e,t,n){var r=0,i=t.length;for(;i>r;r++)ot(e,t[r],n);return n}function St(e,t,r,o){var s,u,l,c,p,f=vt(e);if(!o&&1===f.length){if(u=f[0]=f[0].slice(0),u.length>2&&"ID"===(l=u[0]).type&&n.getById&&9===t.nodeType&&h&&i.relative[u[1].type]){if(t=(i.find.ID(l.matches[0].replace(nt,rt),t)||[])[0],!t)return r;e=e.slice(u.shift().value.length)}s=J.needsContext.test(e)?0:u.length;while(s--){if(l=u[s],i.relative[c=l.type])break;if((p=i.find[c])&&(o=p(l.matches[0].replace(nt,rt),U.test(u[0].type)&&t.parentNode||t))){if(u.splice(s,1),e=o.length&&xt(u),!e)return O.apply(r,o),r;break}}}return a(e,f)(o,t,!h,r,U.test(e)),r}i.pseudos.nth=i.pseudos.eq;function jt(){}jt.prototype=i.filters=i.pseudos,i.setFilters=new jt,n.sortStable=v.split("").sort(S).join("")===v,c(),[0,0].sort(S),n.detectDuplicates=E,x.find=ot,x.expr=ot.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ot.uniqueSort,x.text=ot.getText,x.isXMLDoc=ot.isXML,x.contains=ot.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(p){for(t=e.memory&&p,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(p[0],p[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!a||n&&!u||(r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,H,q=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))x.extend(this.cache[i],t);else for(r in t)o[r]=t[r];return o},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){return t===undefined||t&&"string"==typeof t&&n===undefined?this.get(e,t):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i,o=this.key(e),s=this.cache[o];if(t===undefined)this.cache[o]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):(i=x.camelCase(t),t in s?r=[t,i]:(r=i,r=r in s?[r]:r.match(w)||[])),n=r.length;while(n--)delete s[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){e[this.expando]&&delete this.cache[e[this.expando]]}},L=new F,H=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||H.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return H.access(e,t,n)},_removeData:function(e,t){H.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!H.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.slice(5)),P(i,r,s[r]));H.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:q.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=H.get(e,t),n&&(!r||x.isArray(n)?r=H.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire() +},_queueHooks:function(e,t){var n=t+"queueHooks";return H.get(e,n)||H.access(e,n,{empty:x.Callbacks("once memory").add(function(){H.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t);x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=H.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n\f]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,i="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,s=0,a=x(this),u=t,l=e.match(w)||[];while(o=l[s++])u=i?u:!a.hasClass(o),a[u?"addClass":"removeClass"](o)}else(n===r||"boolean"===n)&&(this.className&&H.set(this,"__className__",this.className),this.className=this.className||e===!1?"":H.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,x(this).val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.bool.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,p,f,h,d,g,m,y=H.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(f=x.event.special[d]||{},d=(o?f.delegateType:f.bindType)||d,f=x.event.special[d]||{},p=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,f.setup&&f.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),f.add&&(f.add.call(e,p),p.handler.guid||(p.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,p):h.push(p),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,p,f,h,d,g,m=H.hasData(e)&&H.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){p=x.event.special[h]||{},h=(r?p.delegateType:p.bindType)||h,f=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=f.length;while(o--)c=f[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(f.splice(o,1),c.selector&&f.delegateCount--,p.remove&&p.remove.call(e,c));s&&!f.length&&(p.teardown&&p.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,H.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,p,f,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),f=x.event.special[d]||{},i||!f.trigger||f.trigger.apply(r,n)!==!1)){if(!i&&!f.noBubble&&!x.isWindow(r)){for(l=f.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:f.bindType||d,p=(H.get(a,"events")||{})[t.type]&&H.get(a,"handle"),p&&p.apply(a,n),p=c&&a[c],p&&x.acceptData(a)&&p.apply&&p.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||f._default&&f._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(H.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,s=e,a=this.fixHooks[i];a||(this.fixHooks[i]=a=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new x.Event(s),t=r.length;while(t--)n=r[t],e[n]=s[n];return e.target||(e.target=o),3===e.target.nodeType&&(e.target=e.target.parentNode),a.filter?a.filter(e,s):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=/^(?:parents|prev(?:Until|All))/,Q=x.expr.match.needsContext,K={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(et(this,e||[],!0))},filter:function(e){return this.pushStack(et(this,e||[],!1))},is:function(e){return!!et(this,"string"==typeof e&&Q.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],s=Q.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function Z(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return Z(e,"nextSibling")},prev:function(e){return Z(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return e.contentDocument||x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(K[e]||x.unique(i),J.test(e)&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,nt=/<([\w:]+)/,rt=/<|&#?\w+;/,it=/<(?:script|style|link)/i,ot=/^(?:checkbox|radio)$/i,st=/checked\s*(?:[^=]|=\s*.checked.)/i,at=/^$|\/(?:java|ecma)script/i,ut=/^true\/(.*)/,lt=/^\s*\s*$/g,ct={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ct.optgroup=ct.option,ct.tbody=ct.tfoot=ct.colgroup=ct.caption=ct.thead,ct.th=ct.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(mt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&dt(mt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(mt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!it.test(e)&&!ct[(nt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(tt,"<$1>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(mt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=f.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,p=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&st.test(d))return this.each(function(r){var i=p.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(mt(r,"script"),ft),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,mt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,ht),l=0;s>l;l++)a=o[l],at.test(a.type||"")&&!H.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(lt,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=mt(a),o=mt(e),r=0,i=o.length;i>r;r++)yt(o[r],s[r]);if(t)if(n)for(o=o||mt(e),s=s||mt(a),r=0,i=o.length;i>r;r++)gt(o[r],s[r]);else gt(e,a);return s=mt(a,"script"),s.length>0&&dt(s,!u&&mt(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,p=e.length,f=t.createDocumentFragment(),h=[];for(;p>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(rt.test(i)){o=o||f.appendChild(t.createElement("div")),s=(nt.exec(i)||["",""])[1].toLowerCase(),a=ct[s]||ct._default,o.innerHTML=a[1]+i.replace(tt,"<$1>")+a[2],l=a[0];while(l--)o=o.firstChild;x.merge(h,o.childNodes),o=f.firstChild,o.textContent=""}else h.push(t.createTextNode(i));f.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=mt(f.appendChild(i),"script"),u&&dt(o),n)){l=0;while(i=o[l++])at.test(i.type||"")&&n.push(i)}return f},cleanData:function(e){var t,n,r,i,o,s,a=x.event.special,u=0;for(;(n=e[u])!==undefined;u++){if(F.accepts(n)&&(o=n[H.expando],o&&(t=H.cache[o]))){if(r=Object.keys(t.events||{}),r.length)for(s=0;(i=r[s])!==undefined;s++)a[i]?x.event.remove(n,i):x.removeEvent(n,i,t.handle);H.cache[o]&&delete H.cache[o]}delete L.cache[n[L.expando]]}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}});function pt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function dt(e,t){var n=e.length,r=0;for(;n>r;r++)H.set(e[r],"globalEval",!t||H.get(t[r],"globalEval"))}function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(H.hasData(e)&&(o=H.access(e),s=H.set(t,o),l=o.events)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var vt,xt,bt=/^(none|table(?!-c[ea]).+)/,wt=/^margin/,Tt=RegExp("^("+b+")(.*)$","i"),Ct=RegExp("^("+b+")(?!px)[a-z%]+$","i"),kt=RegExp("^([+-])=("+b+")","i"),Nt={BODY:"block"},Et={position:"absolute",visibility:"hidden",display:"block"},St={letterSpacing:0,fontWeight:400},jt=["Top","Right","Bottom","Left"],Dt=["Webkit","O","Moz","ms"];function At(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Dt.length;while(i--)if(t=Dt[i]+n,t in e)return t;return r}function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function Ht(t){return e.getComputedStyle(t,null)}function qt(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=H.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&Lt(r)&&(o[s]=H.access(r,"olddisplay",Rt(r.nodeName)))):o[s]||(i=Lt(r),(n&&"none"!==n||!i)&&H.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=Ht(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return qt(this,!0)},hide:function(){return qt(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:Lt(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=vt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=At(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=kt.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=At(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=vt(e,t,r)),"normal"===i&&t in St&&(i=St[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),vt=function(e,t,n){var r,i,o,s=n||Ht(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Ct.test(a)&&wt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ft(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+jt[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+jt[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+jt[o]+"Width",!0,i))):(s+=x.css(e,"padding"+jt[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+jt[o]+"Width",!0,i)));return s}function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Ht(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=vt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Ct.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ft(e,t,n||(s?"border":"content"),r,o)+"px"}function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(xt||x("