Refactor the gitx CLI to use apple events and the scripting bridge

Sending the arguments with the openURL:... message allows the repository document to modify it's UI without the UI flashing between states as it opens.

Covers all the existing functionality of the CLI, but modifies:
    - "--all" "--local" "--branch" change the branch filter
    - cleaned up the usage (help) text and added info on missing commands
    - looks up the full ref name of refs so the name of a branch or tag can be entered (the user can enter "master" instead of "refs/heads/master")

Modified the History Controller to watch for and react to branch filter changes.

The GitX.h file is generated by the 'sdp' tool in a run script build phase called 'Generate Scripting Bridge Header' based on the content of GitX.sdef. It is used by the Scripting Bridge so that other apps (in this case the gitx CLI) can call Applescript commands on GitX in objective-c.
This commit is contained in:
Nathan Kinsinger
2010-08-19 00:20:33 -06:00
parent beaa591b53
commit e60bb3226c
13 changed files with 610 additions and 97 deletions
-3
View File
@@ -9,7 +9,6 @@
#import <Cocoa/Cocoa.h>
#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;
+5 -8
View File
@@ -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];
+69
View File
@@ -0,0 +1,69 @@
/*
* GitX.h
*/
#import <AppKit/AppKit.h>
#import <ScriptingBridge/ScriptingBridge.h>
@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
+166
View File
@@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="GitX Terminology">
<suite name="Standard Suite" code="????" description="Common classes and commands for all applications.">
<command name="open" code="aevtodoc" description="Open a document.">
<direct-parameter description="The file(s) to be opened.">
<type type="file" list="yes"/>
</direct-parameter>
</command>
<command name="close" code="coreclos" description="Close a document.">
<cocoa class="NSCloseCommand"/>
<direct-parameter type="specifier" description="the document(s) or window(s) to close."/>
</command>
<command name="quit" code="aevtquit" description="Quit the application.">
<cocoa class="NSQuitCommand"/>
</command>
<command name="count" code="corecnte" description="Return the number of elements of a particular class within an object.">
<cocoa class="NSCountCommand"/>
<direct-parameter type="specifier" description="The objects to be counted."/>
<parameter name="each" code="kocl" type="type" optional="yes" description="The class of objects to be counted." hidden="yes">
<cocoa key="ObjectClass"/>
</parameter>
<result type="integer" description="The count."/>
</command>
<command name="delete" code="coredelo" description="Delete an object.">
<cocoa class="NSDeleteCommand"/>
<direct-parameter type="specifier" description="The object(s) to delete."/>
</command>
<command name="duplicate" code="coreclon" description="Copy an object.">
<cocoa class="NSCloneCommand"/>
<direct-parameter type="specifier" description="The object(s) to copy."/>
<parameter name="to" code="insh" type="location specifier" description="The location for the new copy or copies." optional="yes">
<cocoa key="ToLocation"/>
</parameter>
<parameter name="with properties" code="prdt" type="record" description="Properties to set in the new copy or copies right away." optional="yes">
<cocoa key="WithProperties"/>
</parameter>
</command>
<command name="exists" code="coredoex" description="Verify that an object exists.">
<cocoa class="NSExistsCommand"/>
<direct-parameter type="any" description="The object(s) to check."/>
<result type="boolean" description="Did the object(s) exist?"/>
</command>
<command name="make" code="corecrel" description="Create a new object.">
<cocoa class="NSCreateCommand"/>
<parameter name="new" code="kocl" type="type" description="The class of the new object.">
<cocoa key="ObjectClass"/>
</parameter>
<parameter name="at" code="insh" type="location specifier" optional="yes" description="The location at which to insert the object.">
<cocoa key="Location"/>
</parameter>
<parameter name="with data" code="data" type="any" optional="yes" description="The initial contents of the object.">
<cocoa key="ObjectData"/>
</parameter>
<parameter name="with properties" code="prdt" type="record" optional="yes" description="The initial values for properties of the object.">
<cocoa key="KeyDictionary"/>
</parameter>
<result type="specifier" description="The new object."/>
</command>
<command name="move" code="coremove" description="Move an object to a new location.">
<cocoa class="NSMoveCommand"/>
<direct-parameter type="specifier" description="The object(s) to move."/>
<parameter name="to" code="insh" type="location specifier" description="The new location for the object(s).">
<cocoa key="ToLocation"/>
</parameter>
</command>
<class name="application" code="capp" description="The application's top-level scripting object.">
<cocoa class="NSApplication"/>
<property name="name" code="pnam" type="text" access="r" description="The name of the application."/>
<property name="frontmost" code="pisf" type="boolean" access="r" description="Is this the active application?">
<cocoa key="isActive"/>
</property>
<property name="version" code="vers" type="text" access="r" description="The version number of the application."/>
<element type="document">
<cocoa key="orderedDocuments"/>
</element>
<element type="window" access="r">
<cocoa key="orderedWindows"/>
</element>
<responds-to name="open">
<cocoa method="handleOpenScriptCommand:"/>
</responds-to>
<responds-to name="quit">
<cocoa method="handleQuitScriptCommand:"/>
</responds-to>
</class>
<class name="document" code="docu" description="A document.">
<cocoa class="NSDocument"/>
<property name="name" code="pnam" type="text" access="r" description="Its name.">
<cocoa key="displayName"/>
</property>
<property name="file" code="file" type="file" access="r" description="Its location on disk, if it has one.">
<cocoa key="fileURL"/>
</property>
<responds-to command="close">
<cocoa method="handleCloseScriptCommand:"/>
</responds-to>
</class>
<class name="window" code="cwin" description="A window.">
<cocoa class="NSWindow"/>
<property name="name" code="pnam" type="text" access="r" description="The title of the window.">
<cocoa key="title"/>
</property>
<property name="id" code="ID " type="integer" access="r" description="The unique identifier of the window.">
<cocoa key="uniqueID"/>
</property>
<property name="index" code="pidx" type="integer" description="The index of the window, ordered front to back.">
<cocoa key="orderedIndex"/>
</property>
<property name="bounds" code="pbnd" type="rectangle" description="The bounding rectangle of the window.">
<cocoa key="boundsAsQDRect"/>
</property>
<property name="closeable" code="hclb" type="boolean" access="r" description="Does the window have a close button?">
<cocoa key="hasCloseBox"/>
</property>
<property name="miniaturizable" code="ismn" type="boolean" access="r" description="Does the window have a minimize button?">
<cocoa key="isMiniaturizable"/>
</property>
<property name="miniaturized" code="pmnd" type="boolean" description="Is the window minimized right now?">
<cocoa key="isMiniaturized"/>
</property>
<property name="resizable" code="prsz" type="boolean" access="r" description="Can the window be resized?">
<cocoa key="isResizable"/>
</property>
<property name="visible" code="pvis" type="boolean" description="Is the window visible right now?">
<cocoa key="isVisible"/>
</property>
<property name="zoomable" code="iszm" type="boolean" access="r" description="Does the window have a zoom button?">
<cocoa key="isZoomable"/>
</property>
<property name="zoomed" code="pzum" type="boolean" description="Is the window zoomed right now?">
<cocoa key="isZoomed"/>
</property>
<property name="document" code="docu" type="document" access="r" description="The document whose contents are displayed in the window."/>
<responds-to name="close">
<cocoa method="handleCloseScriptCommand:"/>
</responds-to>
</class>
</suite>
<suite name="GitX Suite" code="GitX" description="Classes for GitX.">
<command name="show diff" code="GitXShDf" description="Show the supplied diff output in a GitX window.">
<direct-parameter type="text" description="The textual output from a diff tool."/>
</command>
</suite>
</dictionary>
+43 -6
View File
@@ -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 = "<absolute>"; };
913D5E440E55640C00CECEA2 /* gitx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = gitx.m; sourceTree = "<group>"; };
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 = "<group>"; };
913D5E5E0E556A9300CECEA2 /* PBCLIProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCLIProxy.m; sourceTree = "<group>"; };
91B103CA0E898EC300C84364 /* PBIconAndTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBIconAndTextCell.h; sourceTree = "<group>"; };
91B103CB0E898EC300C84364 /* PBIconAndTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBIconAndTextCell.m; sourceTree = "<group>"; };
93CB42C00EAB7B2200530609 /* PBGitDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitDefaults.h; sourceTree = "<group>"; };
@@ -307,6 +307,8 @@
D858108111274D28007F254B /* RemoteBranch.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RemoteBranch.png; path = Images/RemoteBranch.png; sourceTree = "<group>"; };
D858108211274D28007F254B /* Tag.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Tag.png; path = Images/Tag.png; sourceTree = "<group>"; };
D85B93F610E51279007F3C28 /* PBGitRefish.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRefish.h; sourceTree = "<group>"; };
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 = "<group>"; };
D8A4BB6D11337D5C00E92D51 /* PBGitGradientBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGradientBarView.h; sourceTree = "<group>"; };
D8A4BB6E11337D5C00E92D51 /* PBGitGradientBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitGradientBarView.m; sourceTree = "<group>"; };
D8A4BD041134AD2900E92D51 /* CherryPickTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = CherryPickTemplate.png; path = Images/CherryPickTemplate.png; sourceTree = "<group>"; };
@@ -321,6 +323,10 @@
D8E3B38110DD4E2C001096A3 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCreateTagSheet.xib; sourceTree = "<group>"; };
D8EB6168122F643E00FCCAF4 /* GitXRelativeDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXRelativeDateFormatter.h; sourceTree = "<group>"; };
D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXRelativeDateFormatter.m; sourceTree = "<group>"; };
D8F01C4A12182F19007F729F /* GitX.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.sdef; path = GitX.sdef; sourceTree = "<group>"; };
D8F01D511218A164007F729F /* NSApplication+GitXScripting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSApplication+GitXScripting.h"; sourceTree = "<group>"; };
D8F01D521218A164007F729F /* NSApplication+GitXScripting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSApplication+GitXScripting.m"; sourceTree = "<group>"; };
D8F01D841218A406007F729F /* GitX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitX.h; sourceTree = "<group>"; };
D8FBCF17115FA20C0098676A /* PBGitSHA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSHA.h; sourceTree = "<group>"; };
D8FBCF18115FA20C0098676A /* PBGitSHA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSHA.m; sourceTree = "<group>"; };
D8FDD9F611432A12005647F6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCloneRepositoryPanel.xib; sourceTree = "<group>"; };
@@ -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 = "<group>";
@@ -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 = "<group>";
};
D89E9B4C1218C22A0097A90B /* GitXScripting */ = {
isa = PBXGroup;
children = (
D8F01D841218A406007F729F /* GitX.h */,
D89E9B4F1218C2750097A90B /* GitXScriptingConstants.h */,
D8F01D511218A164007F729F /* NSApplication+GitXScripting.h */,
D8F01D521218A164007F729F /* NSApplication+GitXScripting.m */,
);
name = GitXScripting;
sourceTree = "<group>";
};
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;
};
+12
View File
@@ -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'
+4
View File
@@ -89,5 +89,9 @@
</dict>
</dict>
</array>
<key>NSAppleScriptEnabled</key>
<true/>
<key>OSAScriptingDefinition</key>
<string>GitX.sdef</string>
</dict>
</plist>
+16
View File
@@ -0,0 +1,16 @@
//
// NSApplication+GitXScripting.h
// GitX
//
// Created by Nathan Kinsinger on 8/15/10.
// Copyright 2010 Nathan Kinsinger. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface NSApplication (GitXScripting)
- (void)showDiffScriptCommand:(NSScriptCommand *)command;
@end
+25
View File
@@ -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
+8
View File
@@ -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];
+1
View File
@@ -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;
+133
View File
@@ -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: <SHA-1 ID> <space> <reference name>
// 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
+128 -80
View File
@@ -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 <revlist options>\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<branch> show specific branch\n");
printf("\t -- <path> show commits touching paths\n");
printf(" <branch> select specific branch or tag\n");
printf(" -- <path(s)> show commits touching paths\n");
printf(" -S<string> show commits that introduce or remove an instance of <string>\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 [<common diff options>] <commit>{0,2} [--] [<path>...]\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<GitXCliToolProtocol> 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<GitXCliToolProtocol> 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;
}