diff --git a/ApplicationController.h b/ApplicationController.h index 4305662..f368344 100644 --- a/ApplicationController.h +++ b/ApplicationController.h @@ -9,7 +9,6 @@ #import #import "PBGitRepository.h" -@class PBCLIProxy; @class PBCloneRepositoryPanel; @interface ApplicationController : NSObject @@ -20,10 +19,8 @@ NSManagedObjectModel *managedObjectModel; NSManagedObjectContext *managedObjectContext; - PBCLIProxy *cliProxy; PBCloneRepositoryPanel *cloneRepositoryPanel; } -@property (retain) PBCLIProxy* cliProxy; - (NSPersistentStoreCoordinator *)persistentStoreCoordinator; - (NSManagedObjectModel *)managedObjectModel; diff --git a/ApplicationController.m b/ApplicationController.m index 2a3d2ae..c73b524 100644 --- a/ApplicationController.m +++ b/ApplicationController.m @@ -10,7 +10,6 @@ #import "PBGitRevisionCell.h" #import "PBGitWindowController.h" #import "PBRepositoryDocumentController.h" -#import "PBCLIProxy.h" #import "PBServicesController.h" #import "PBGitXProtocol.h" #import "PBPrefsWindowController.h" @@ -20,7 +19,6 @@ #import "Sparkle/SUUpdater.h" @implementation ApplicationController -@synthesize cliProxy; - (ApplicationController*)init { @@ -28,13 +26,12 @@ [NSApp activateIgnoringOtherApps:YES]; #endif - if(self = [super init]) { - if(![[NSBundle bundleWithPath:@"/System/Library/Frameworks/Quartz.framework/Frameworks/QuickLookUI.framework"] load]) - if(![[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load]) - NSLog(@"Could not load QuickLook"); + if(!(self = [super init])) + return nil; - self.cliProxy = [PBCLIProxy new]; - } + if(![[NSBundle bundleWithPath:@"/System/Library/Frameworks/Quartz.framework/Frameworks/QuickLookUI.framework"] load]) + if(![[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load]) + NSLog(@"Could not load QuickLook"); /* Value Transformers */ NSValueTransformer *transformer = [[PBNSURLPathUserDefaultsTransfomer alloc] init]; diff --git a/GitX.h b/GitX.h new file mode 100644 index 0000000..edae903 --- /dev/null +++ b/GitX.h @@ -0,0 +1,69 @@ +/* + * GitX.h + */ + +#import +#import + + +@class GitXApplication, GitXDocument, GitXWindow; + + + +/* + * 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. + +- (void) open:(NSArray *)x; // Open a document. +- (void) quit; // Quit the application. +- (BOOL) exists:(id)x; // Verify that an object exists. +- (void) showDiff:(NSString *)x; // Show the supplied diff output in a GitX window. + +@end + +// A document. +@interface GitXDocument : SBObject + +@property (copy, readonly) NSString *name; // Its name. +@property (copy, readonly) NSURL *file; // Its location on disk, if it has one. + +- (void) close; // Close 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. + +@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. + +- (void) close; // Close 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. + +@end + diff --git a/GitX.sdef b/GitX.sdef new file mode 100644 index 0000000..a0a0642 --- /dev/null +++ b/GitX.sdef @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 469427b..5cb9bd1 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -41,7 +41,6 @@ 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 */; }; @@ -70,6 +69,7 @@ D858108511274D28007F254B /* Tag.png in Resources */ = {isa = PBXBuildFile; fileRef = D858108211274D28007F254B /* Tag.png */; }; D85B939310E3D8B4007F3C28 /* PBCreateBranchSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = D85B939210E3D8B4007F3C28 /* PBCreateBranchSheet.xib */; }; D889EB3110E6BCBB00F08413 /* PBCreateTagSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = D889EB3010E6BCBB00F08413 /* PBCreateTagSheet.xib */; }; + D89E9B141218BA260097A90B /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */; }; D8A4BB6F11337D5C00E92D51 /* PBGitGradientBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A4BB6E11337D5C00E92D51 /* PBGitGradientBarView.m */; }; D8A4BD071134AD2900E92D51 /* CherryPickTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD041134AD2900E92D51 /* CherryPickTemplate.png */; }; D8A4BD081134AD2900E92D51 /* MergeTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD051134AD2900E92D51 /* MergeTemplate.png */; }; @@ -78,6 +78,8 @@ D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */; }; D8E3B34D10DCA958001096A3 /* PBCreateTagSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */; }; D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */; }; + D8F01C4B12182F19007F729F /* GitX.sdef in Resources */ = {isa = PBXBuildFile; fileRef = D8F01C4A12182F19007F729F /* GitX.sdef */; }; + D8F01D531218A164007F729F /* NSApplication+GitXScripting.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F01D521218A164007F729F /* NSApplication+GitXScripting.m */; }; D8FBCF19115FA20C0098676A /* PBGitSHA.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FBCF18115FA20C0098676A /* PBGitSHA.m */; }; D8FDD9F711432A12005647F6 /* PBCloneRepositoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8FDD9F511432A12005647F6 /* PBCloneRepositoryPanel.xib */; }; D8FDDA6A114335E8005647F6 /* PBGitSVBranchItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FDDA5D114335E8005647F6 /* PBGitSVBranchItem.m */; }; @@ -262,8 +264,6 @@ 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 = ""; }; @@ -307,6 +307,8 @@ D858108111274D28007F254B /* RemoteBranch.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RemoteBranch.png; path = Images/RemoteBranch.png; sourceTree = ""; }; D858108211274D28007F254B /* Tag.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Tag.png; path = Images/Tag.png; sourceTree = ""; }; D85B93F610E51279007F3C28 /* PBGitRefish.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRefish.h; sourceTree = ""; }; + D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = System/Library/Frameworks/ScriptingBridge.framework; sourceTree = SDKROOT; }; + D89E9B4F1218C2750097A90B /* GitXScriptingConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXScriptingConstants.h; sourceTree = ""; }; D8A4BB6D11337D5C00E92D51 /* PBGitGradientBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGradientBarView.h; sourceTree = ""; }; D8A4BB6E11337D5C00E92D51 /* PBGitGradientBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitGradientBarView.m; sourceTree = ""; }; D8A4BD041134AD2900E92D51 /* CherryPickTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = CherryPickTemplate.png; path = Images/CherryPickTemplate.png; sourceTree = ""; }; @@ -321,6 +323,10 @@ D8E3B38110DD4E2C001096A3 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCreateTagSheet.xib; sourceTree = ""; }; D8EB6168122F643E00FCCAF4 /* GitXRelativeDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXRelativeDateFormatter.h; sourceTree = ""; }; D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXRelativeDateFormatter.m; sourceTree = ""; }; + D8F01C4A12182F19007F729F /* GitX.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.sdef; path = GitX.sdef; sourceTree = ""; }; + D8F01D511218A164007F729F /* NSApplication+GitXScripting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSApplication+GitXScripting.h"; sourceTree = ""; }; + D8F01D521218A164007F729F /* NSApplication+GitXScripting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSApplication+GitXScripting.m"; sourceTree = ""; }; + D8F01D841218A406007F729F /* GitX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitX.h; sourceTree = ""; }; D8FBCF17115FA20C0098676A /* PBGitSHA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSHA.h; sourceTree = ""; }; D8FBCF18115FA20C0098676A /* PBGitSHA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSHA.m; sourceTree = ""; }; D8FDD9F611432A12005647F6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCloneRepositoryPanel.xib; sourceTree = ""; }; @@ -478,6 +484,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D89E9B141218BA260097A90B /* ScriptingBridge.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -548,6 +555,7 @@ children = ( F5886A080ED5D26B0066E74C /* SpeedTest */, 913D5E420E5563FD00CECEA2 /* cli */, + D89E9B4C1218C22A0097A90B /* GitXScripting */, F57CC43E0E05E472000472E2 /* Aux */, F57CC3850E05DDC1000472E2 /* Controllers */, F56174540E05887E001DCD79 /* Git */, @@ -561,6 +569,7 @@ 19C28FACFE9D520D11CA2CBB /* Products */, F5886A120ED5D33D0066E74C /* SpeedTest-Info.plist */, F567CC3A106E6B910059BB9D /* GitXTesting-Info.plist */, + D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */, ); name = GitTest; sourceTree = ""; @@ -604,6 +613,7 @@ D26DC6440E782C9000C777B2 /* gitx.icns */, 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + D8F01C4A12182F19007F729F /* GitX.sdef */, F5B721C20E05CF7E00AF29DC /* MainMenu.xib */, 911111E00E58BD5A00BF76B4 /* RepositoryWindow.xib */, F5E424100EA3E4D60046E362 /* PBDiffWindow.xib */, @@ -660,8 +670,6 @@ 913D5E420E5563FD00CECEA2 /* cli */ = { isa = PBXGroup; children = ( - 913D5E5D0E556A9300CECEA2 /* PBCLIProxy.h */, - 913D5E5E0E556A9300CECEA2 /* PBCLIProxy.m */, 913D5E440E55640C00CECEA2 /* gitx.m */, ); name = cli; @@ -688,6 +696,17 @@ name = Sheets; sourceTree = ""; }; + D89E9B4C1218C22A0097A90B /* GitXScripting */ = { + isa = PBXGroup; + children = ( + D8F01D841218A406007F729F /* GitX.h */, + D89E9B4F1218C2750097A90B /* GitXScriptingConstants.h */, + D8F01D511218A164007F729F /* NSApplication+GitXScripting.h */, + D8F01D521218A164007F729F /* NSApplication+GitXScripting.m */, + ); + name = GitXScripting; + sourceTree = ""; + }; D8FDDA58114335B0005647F6 /* Source View Items */ = { isa = PBXGroup; children = ( @@ -997,6 +1016,7 @@ 8D11072E0486CEB800E47090 /* Frameworks */, F580E6BD0E73329C009E2D3F /* CopyFiles */, F5CF04A20EAE696C00D75C81 /* Copy HTML files */, + D81E15ED121CE83D00269E61 /* Scripting Bridge Header Script */, ); buildRules = ( ); @@ -1133,6 +1153,7 @@ D8A4BD091134AD2900E92D51 /* RebaseTemplate.png in Resources */, D828AEEC112F411100F09D11 /* CloneRepositoryTemplate.png in Resources */, D8022FE811E124A0003C21F6 /* PBGitXMessageSheet.xib in Resources */, + D8F01C4B12182F19007F729F /* GitX.sdef in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1146,6 +1167,22 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + D81E15ED121CE83D00269E61 /* Scripting Bridge Header Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/GitX.sdef", + ); + name = "Scripting Bridge Header Script "; + outputPaths = ( + "$(SRCROOT)/GitX.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Build the scripting bridge header GitX.h if the GitX.sdef file changes\nsdef $TARGET_BUILD_DIR/GitX.app | sdp -fh --basename GitX"; + }; F56439F60F792B2100A579C2 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1207,7 +1244,6 @@ 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 */, @@ -1264,6 +1300,7 @@ D8FBCF19115FA20C0098676A /* PBGitSHA.m in Sources */, D8022FED11E124C8003C21F6 /* PBGitXMessageSheet.m in Sources */, D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */, + D8F01D531218A164007F729F /* NSApplication+GitXScripting.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/GitXScriptingConstants.h b/GitXScriptingConstants.h new file mode 100644 index 0000000..cba0ce8 --- /dev/null +++ b/GitXScriptingConstants.h @@ -0,0 +1,12 @@ +// +// GitXScriptingConstants.h +// GitX +// +// Created by Nathan Kinsinger on 8/15/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#define kGitXBundleIdentifier @"nl.frim.GitX" + + +#define kGitXAEKeyArgumentsList 'ARGS' \ No newline at end of file diff --git a/Info.plist b/Info.plist index b7a8de4..d7e5e5b 100644 --- a/Info.plist +++ b/Info.plist @@ -89,5 +89,9 @@ + NSAppleScriptEnabled + + OSAScriptingDefinition + GitX.sdef diff --git a/NSApplication+GitXScripting.h b/NSApplication+GitXScripting.h new file mode 100644 index 0000000..d5cab8c --- /dev/null +++ b/NSApplication+GitXScripting.h @@ -0,0 +1,16 @@ +// +// 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; + +@end diff --git a/NSApplication+GitXScripting.m b/NSApplication+GitXScripting.m new file mode 100644 index 0000000..7c8489c --- /dev/null +++ b/NSApplication+GitXScripting.m @@ -0,0 +1,25 @@ +// +// NSApplication+GitXScripting.m +// GitX +// +// Created by Nathan Kinsinger on 8/15/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "NSApplication+GitXScripting.h" +#import "PBDiffWindowController.h" + + +@implementation NSApplication (GitXScripting) + +- (void)showDiffScriptCommand:(NSScriptCommand *)command +{ + NSString *diffText = [command directParameter]; + if (diffText) { + PBDiffWindowController *diffController = [[PBDiffWindowController alloc] initWithDiff:diffText]; + [diffController showWindow:nil]; + [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; + } +} + +@end diff --git a/PBGitHistoryController.m b/PBGitHistoryController.m index 89cd1ab..3f08f65 100644 --- a/PBGitHistoryController.m +++ b/PBGitHistoryController.m @@ -50,6 +50,7 @@ [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]; @@ -240,6 +241,12 @@ return; } + if ([(NSString *)context isEqualToString:@"branchFilterChange"]) { + [PBGitDefaults setBranchFilter:repository.currentBranchFilter]; + [self updateBranchFilterMatrix]; + return; + } + if([(NSString *)context isEqualToString:@"updateCommitCount"] || [(NSString *)context isEqualToString:@"revisionListUpdating"]) { [self updateStatus]; @@ -454,6 +461,7 @@ [repository.revisionList removeObserver:self forKeyPath:@"isUpdating"]; [repository removeObserver:self forKeyPath:@"currentBranch"]; [repository removeObserver:self forKeyPath:@"refs"]; + [repository removeObserver:self forKeyPath:@"currentBranchFilter"]; } [webHistoryController closeView]; diff --git a/PBGitRepository.h b/PBGitRepository.h index 25176cd..86b094a 100644 --- a/PBGitRepository.h +++ b/PBGitRepository.h @@ -104,6 +104,7 @@ static NSString * PBStringFromBranchFilterType(PBGitXBranchFilterType type) { - (BOOL)isRefOnHeadBranch:(PBGitRef *)testRef; - (BOOL)checkRefFormat:(NSString *)refName; - (BOOL)refExists:(PBGitRef *)ref; +- (PBGitRef *)refForName:(NSString *)name; - (NSArray *) remotes; - (BOOL) hasRemotes; diff --git a/PBGitRepository.m b/PBGitRepository.m index 6f93a71..48a70f4 100644 --- a/PBGitRepository.m +++ b/PBGitRepository.m @@ -18,6 +18,7 @@ #import "PBRemoteProgressSheet.h" #import "PBGitRevList.h" #import "PBGitDefaults.h" +#import "GitXScriptingConstants.h" NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain"; @@ -429,6 +430,31 @@ NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain"; return NO; return YES; } + +// 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; + + int retValue = 1; + NSString *output = [self outputInWorkdirForArguments:[NSArray arrayWithObjects:@"show-ref", name, nil] retValue:&retValue]; + if (retValue) + return nil; + + // 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) { + NSString *refName = [refList objectAtIndex:1]; + return [PBGitRef refFromString:refName]; + } + + return nil; +} // Returns either this object, or an existing, equal object - (PBGitRevSpecifier*) addBranch:(PBGitRevSpecifier*)branch @@ -876,6 +902,113 @@ NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain"; } +#pragma mark GitX Scripting + +- (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 refForName:[arguments lastObject]]; + if (refArgument) + revListSpecifier = [[PBGitRevSpecifier alloc] initWithRef:refArgument]; + } + + if (!revListSpecifier) + revListSpecifier = [[PBGitRevSpecifier alloc] initWithParameters:arguments]; + + self.currentBranch = [self addBranch:revListSpecifier]; + [PBGitDefaults setShowStageView:NO]; + [self.windowController showHistoryView:self]; +} + +- (void)handleBranchFilterEventForFilter:(PBGitXBranchFilterType)filter additionalArguments:(NSMutableArray *)arguments +{ + self.currentBranchFilter = filter; + [PBGitDefaults setShowStageView:NO]; + [self.windowController showHistoryView:self]; + + // treat any additional arguments as a rev-list specifier + if ([arguments count] > 1) { + [arguments removeObjectAtIndex:0]; + [self handleRevListArguments:arguments]; + } +} + +- (void)handleGitXScriptingArguments:(NSAppleEventDescriptor *)argumentsList +{ + NSMutableArray *arguments = [NSMutableArray array]; + uint argumentsIndex = 1; // AppleEvent list descriptor's are one based + while(1) { + NSAppleEventDescriptor *arg = [argumentsList descriptorAtIndex:argumentsIndex++]; + if (arg) + [arguments addObject:[arg stringValue]]; + else + break; + } + + if (![arguments count]) + return; + + 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]; +} + +// see if the current appleEvent has the command line arguments from the gitx cli +// this could be from an openApplication or an openDocument apple event +// when opening a repository this is called before the sidebar controller gets it's awakeFromNib: message +// if the repository is already open then this is also a good place to catch the event as the window is about to be brought forward +- (void)showWindows +{ + NSAppleEventDescriptor *currentAppleEvent = [[NSAppleEventManager sharedAppleEventManager] currentAppleEvent]; + + if (currentAppleEvent) { + NSAppleEventDescriptor *eventRecord = [currentAppleEvent paramDescriptorForKeyword:keyAEPropData]; + + // on app launch there may be many repositories opening, so double check that this is the right repo + NSString *path = [[eventRecord paramDescriptorForKeyword:typeFileURL] stringValue]; + if (path) { + if ([[PBGitRepository gitDirForURL:[NSURL URLWithString:path]] isEqual:[self fileURL]]) { + NSAppleEventDescriptor *argumentsList = [eventRecord paramDescriptorForKeyword:kGitXAEKeyArgumentsList]; + [self handleGitXScriptingArguments:argumentsList]; + + // showWindows may be called more than once during app launch so remove the CLI data after we handle the event + [currentAppleEvent removeDescriptorWithKeyword:keyAEPropData]; + } + } + } + + [super showWindows]; +} + + #pragma mark low level - (int) returnValueForCommand:(NSString *)cmd diff --git a/gitx.m b/gitx.m index 464a5a0..8c3ef77 100644 --- a/gitx.m +++ b/gitx.m @@ -6,61 +6,52 @@ // Copyright 2008 __MyCompanyName__. All rights reserved. // -#import "PBCLIProxy.h" #import "PBGitBinary.h" #import "PBEasyPipe.h" +#import "GitXScriptingConstants.h" +#import "GitX.h" -NSDistantObject* connect() -{ - id proxy = [NSConnection rootProxyForConnectionWithRegisteredName:ConnectionName host:nil]; - [proxy setProtocolForProxy:@protocol(GitXCliToolProtocol)]; - return proxy; -} -NSDistantObject *createProxy() -{ - NSDistantObject *proxy = connect(); - if (proxy) - return proxy; - - // The connection failed, so try to launch the app - [[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier: @"nl.frim.GitX" - options: NSWorkspaceLaunchWithoutActivation - additionalEventParamDescriptor: nil - launchIdentifier: nil]; - - // Now attempt to connect, allowing the app time to startup - int attempt; - for (attempt = 0; proxy == nil && attempt < 50; ++attempt) { - if ( (proxy = connect()) != nil ) - return proxy; - - usleep(15000); - } - - // not succesful! - fprintf(stderr, "Couldn't connect to app server!\n"); - exit(1); - return nil; -} +#pragma mark Commands handled locally void usage(char const *programName) { - printf("Usage: %s (--help|--version)\n", programName); - printf(" or: %s (--commit|-h)\n", 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("\n"); - printf("\t-h, --help print this help\n"); - printf("\t--commit, -c start GitX in commit mode\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("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("\tSee 'man git-log' and 'man git-rev-list' for options you can pass to gitx\n"); + printf(" See 'man git-log' and 'man git-rev-list' for options you can pass to gitx\n"); printf("\n"); - printf("\t--all show all branches\n"); - printf("\t show specific branch\n"); - printf("\t -- show commits touching paths\n"); + printf(" select specific branch or tag\n"); + printf(" -- show commits touching paths\n"); + printf(" -S show commits that introduce or remove an instance of \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"); exit(1); } @@ -82,36 +73,107 @@ void git_path() exit(101); NSString *path = [[PBGitBinary path] stringByDeletingLastPathComponent]; - printf("%s", [path UTF8String]); + printf("%s\n", [path UTF8String]); exit(0); } -void handleSTDINDiff(id proxy) + +#pragma mark - +#pragma mark Commands sent to GitX + +void handleSTDINDiff() { NSFileHandle *handle = [NSFileHandle fileHandleWithStandardInput]; NSData *data = [handle readDataToEndOfFile]; - NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSString *diff = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (string && [string length] > 0) { - [proxy openDiffWindowWithDiff:string]; - exit(0); + if (diff && [diff length] > 0) { + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp showDiff:diff]; } + + exit(0); } -void handleDiffWithArguments(NSArray *arguments, NSString *directory, id proxy) +void handleDiffWithArguments(NSURL *repositoryURL, NSMutableArray *arguments) { - int ret; - arguments = [[NSArray arrayWithObject:@"diff"] arrayByAddingObjectsFromArray:arguments]; - NSString *diff = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:directory retValue:&ret]; - if (ret) { - printf("Invalid diff command\n"); + [arguments insertObject:@"diff" atIndex:0]; + + int retValue = 1; + NSString *diffOutput = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:[repositoryURL path] retValue:&retValue]; + if (retValue) { + // if there is an error diffOutput should have the error output from git + if (diffOutput) + printf("%s\n", [diffOutput UTF8String]); + else + printf("Invalid diff command [%d]\n", retValue); exit(3); } - [proxy openDiffWindowWithDiff:diff]; + GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier]; + [gitXApp showDiff:diffOutput]; + exit(0); } +void handleOpenRepository(NSURL *repositoryURL, NSMutableArray *arguments) +{ + // if there are command line arguments send them to GitX through an Apple Event + // the recordDescriptor will be stored in keyAEPropData inside the openDocument or openApplication event + NSAppleEventDescriptor *recordDescriptor = nil; + if ([arguments count]) { + recordDescriptor = [NSAppleEventDescriptor recordDescriptor]; + + NSAppleEventDescriptor *listDescriptor = [NSAppleEventDescriptor listDescriptor]; + uint listIndex = 1; // AppleEvent list descriptor's are one based + for (NSString *argument in arguments) + [listDescriptor insertDescriptor:[NSAppleEventDescriptor descriptorWithString:argument] atIndex:listIndex++]; + + [recordDescriptor setParamDescriptor:listDescriptor forKeyword:kGitXAEKeyArgumentsList]; + + // this is used as a double check in GitX + NSAppleEventDescriptor *url = [NSAppleEventDescriptor descriptorWithString:[repositoryURL absoluteString]]; + [recordDescriptor setParamDescriptor:url forKeyword:typeFileURL]; + } + + // use NSWorkspace to open GitX and send the arguments + // this allows the repository document to modify itself before it shows it's GUI + BOOL didOpenURLs = [[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:repositoryURL] + withAppBundleIdentifier:kGitXBundleIdentifier + options:0 + additionalEventParamDescriptor:recordDescriptor + launchIdentifiers:NULL]; + if (!didOpenURLs) { + printf("Unable to open GitX.app\n"); + exit(2); + } +} + + +#pragma mark - +#pragma mark main + +NSURL *workingDirectoryURL() +{ + NSString *path = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"]; + + NSURL *url = [NSURL fileURLWithPath:path isDirectory:YES]; + if (!url) { + printf("Unable to create url to path: %s\n", [path UTF8String]); + exit(2); + } + + return url; +} + +NSMutableArray *argumentsArray() +{ + NSMutableArray *arguments = [[[NSProcessInfo processInfo] arguments] mutableCopy]; + [arguments removeObjectAtIndex:0]; // url to executable path is not needed + + return arguments; +} + int main(int argc, const char** argv) { if (argc >= 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) @@ -121,42 +183,28 @@ int main(int argc, const char** argv) if (argc >= 2 && !strcmp(argv[1], "--git-path")) git_path(); + // From here on everything needs to access git, so make sure it's installed if (![PBGitBinary path]) { printf("%s\n", [[PBGitBinary notFoundError] cStringUsingEncoding:NSUTF8StringEncoding]); exit(2); } - // Attempt to connect to the app - id proxy = createProxy(); - - // Create arguments - argv++; argc--; - NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:argc]; - int i = 0; - for (i = 0; i < argc; i++) - [arguments addObject: [NSString stringWithUTF8String:argv[i]]]; - + // gitx can be used to pipe diff output to be displayed in GitX if (!isatty(STDIN_FILENO) && fdopen(STDIN_FILENO, "r")) - handleSTDINDiff(proxy); + handleSTDINDiff(); - // From this point, we require a working directory - NSString *pwd = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"]; - if (!pwd) - exit(2); + // From this point, we require a working directory and the arguments + NSMutableArray *arguments = argumentsArray(); + NSURL *wdURL = workingDirectoryURL(); if ([arguments count] > 0 && ([[arguments objectAtIndex:0] isEqualToString:@"--diff"] || - [[arguments objectAtIndex:0] isEqualToString:@"-d"])) - handleDiffWithArguments([arguments subarrayWithRange:NSMakeRange(1, [arguments count] - 1)], pwd, proxy); - - // No diff, just open the current dir - NSURL* url = [NSURL fileURLWithPath:pwd]; - NSError* error = nil; - - if (![proxy openRepository:url arguments: arguments error:&error]) { - fprintf(stderr, "Error opening repository at %s\n", [[url path] UTF8String]); - if (error) - fprintf(stderr, "\t%s\n", [[error localizedFailureReason] UTF8String]); + [[arguments objectAtIndex:0] isEqualToString:@"-d"])) { + [arguments removeObjectAtIndex:0]; + handleDiffWithArguments(wdURL, arguments); } + // No commands handled by gitx, open the current dir in GitX with the arguments + handleOpenRepository(wdURL, arguments); + return 0; }