mirror of
https://github.com/kennethreitz-archive/gitx.git
synced 2026-06-05 23:40:18 +00:00
384 lines
13 KiB
Objective-C
384 lines
13 KiB
Objective-C
//
|
|
// gitx.m
|
|
// GitX
|
|
//
|
|
// Created by Ciarán Walsh on 15/08/2008.
|
|
// Copyright 2008 __MyCompanyName__. All rights reserved.
|
|
//
|
|
|
|
#import "PBGitBinary.h"
|
|
#import "PBEasyPipe.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 <revlist options>\n", programName);
|
|
printf(" or: %s (--diff)\n", programName);
|
|
printf(" or: %s (--init)\n", programName);
|
|
printf(" or: %s (--clone <repository> [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=<path> [gitx commands]\n");
|
|
printf(" send the gitx commands to the repository located at <path>\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(" <branch> select specific branch or tag\n");
|
|
printf(" -- <path(s)> 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 [<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");
|
|
printf("\n");
|
|
printf("Search\n");
|
|
printf("\n");
|
|
printf(" -s<string>, --search=<string>\n");
|
|
printf(" search for string in Subject, Author or SHA\n");
|
|
printf(" -S<string>, --Search=<string>\n");
|
|
printf(" commits that introduce or remove an instance of <string>\n");
|
|
printf(" -r<regex>, --regex=<regex>\n");
|
|
printf(" commits that introduce or remove strings that match <regex>\n");
|
|
printf(" -p<file path>, --path=<file 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 <repository URL> [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 bundleForClass:[PBGitBinary class]] infoDictionary] valueForKey:@"CFBundleVersion"];
|
|
NSString *gitVersion = [[[NSBundle bundleForClass:[PBGitBinary class]] infoDictionary] valueForKey:@"CFBundleGitVersion"];
|
|
printf("GitX version %s (%s)\n", [version UTF8String], [gitVersion UTF8String]);
|
|
if ([PBGitBinary path])
|
|
printf("Using git found at %s, version %s\n", [[PBGitBinary path] UTF8String], [[PBGitBinary version] UTF8String]);
|
|
else
|
|
printf("GitX cannot find a git binary\n");
|
|
exit(1);
|
|
}
|
|
|
|
void git_path()
|
|
{
|
|
if (![PBGitBinary path])
|
|
exit(101);
|
|
|
|
NSString *path = [[PBGitBinary path] stringByDeletingLastPathComponent];
|
|
printf("%s\n", [path UTF8String]);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
#pragma mark Commands sent to GitX
|
|
|
|
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 showDiff:diff];
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
void handleDiffWithArguments(NSURL *repositoryURL, NSArray *arguments)
|
|
{
|
|
arguments = [[NSArray arrayWithObjects:@"diff", @"--no-ext-diff", nil] arrayByAddingObjectsFromArray:arguments];
|
|
|
|
int retValue = 1;
|
|
NSString *diffOutput = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:[repositoryURL path] retValue:&retValue];
|
|
if (retValue) {
|
|
// if there is an error diffOutput should have the error output from git
|
|
if (diffOutput)
|
|
printf("%s\n", [diffOutput UTF8String]);
|
|
else
|
|
printf("Invalid diff command [%d]\n", retValue);
|
|
exit(3);
|
|
}
|
|
|
|
GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier];
|
|
[gitXApp showDiff:diffOutput];
|
|
|
|
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:NSWorkspaceLaunchDefault
|
|
additionalEventParamDescriptor:recordDescriptor
|
|
launchIdentifiers:NULL];
|
|
if (!didOpenURLs) {
|
|
printf("Unable to open GitX.app\n");
|
|
exit(2);
|
|
}
|
|
}
|
|
|
|
void handleInit(NSURL *repositoryURL)
|
|
{
|
|
GitXApplication *gitXApp = [SBApplication applicationWithBundleIdentifier:kGitXBundleIdentifier];
|
|
[gitXApp initRepository: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 kGitXBasicSeachMode;
|
|
|
|
if ([argument hasPrefix:kShortPickaxeSearch] || [argument hasPrefix:kPickaxeSearch])
|
|
return kGitXPickaxeSearchMode;
|
|
|
|
if ([argument hasPrefix:kShortRegexSearch] || [argument hasPrefix:kRegexSearch])
|
|
return kGitXRegexSearchMode;
|
|
|
|
if ([argument hasPrefix:kShortPathSearch] || [argument hasPrefix:kPathSearch])
|
|
return kGitXPathSearchMode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
GitXDocument *documentForURL(SBElementArray *documents, NSURL *URL)
|
|
{
|
|
NSString *path = [URL path];
|
|
|
|
for (GitXDocument *document in documents) {
|
|
NSString *documentPath = [[document file] path];
|
|
if ([[documentPath lastPathComponent] isEqualToString:@".git"])
|
|
documentPath = [documentPath stringByDeletingLastPathComponent];
|
|
|
|
if ([documentPath isEqualToString:path])
|
|
return document;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
void handleGitXSearch(NSURL *repositoryURL, NSMutableArray *arguments)
|
|
{
|
|
NSString *searchString = [arguments componentsJoinedByString:@" "];
|
|
NSInteger mode = searchModeForCommandLineArgument(searchString);
|
|
|
|
// 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 *workingDirectoryURL(NSMutableArray *arguments)
|
|
{
|
|
NSString *path = nil;
|
|
|
|
if ([arguments count] && [[arguments objectAtIndex:0] hasPrefix:kGitDirPrefix]) {
|
|
path = [[[arguments objectAtIndex:0] substringFromIndex:[kGitDirPrefix length]] stringByStandardizingPath];
|
|
|
|
// the path must exist and point to a directory
|
|
BOOL isDirectory = YES;
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory] || !isDirectory) {
|
|
if (!isDirectory)
|
|
printf("Fatal: --git-dir path does not point to a directory.\n");
|
|
else
|
|
printf("Fatal: --git-dir path does not exist.\n");
|
|
printf("Cannot open git repository at path: '%s'\n", [path UTF8String]);
|
|
exit(2);
|
|
}
|
|
|
|
// remove the git-dir argument
|
|
[arguments removeObjectAtIndex:0];
|
|
} else {
|
|
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")))
|
|
usage(argv[0]);
|
|
if (argc >= 2 && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v")))
|
|
version_info();
|
|
if (argc >= 2 && !strcmp(argv[1], "--git-path"))
|
|
git_path();
|
|
|
|
// From here on everything needs to access git, so make sure it's installed
|
|
if (![PBGitBinary path]) {
|
|
printf("%s\n", [[PBGitBinary notFoundError] cStringUsingEncoding:NSUTF8StringEncoding]);
|
|
exit(2);
|
|
}
|
|
|
|
// gitx can be used to pipe diff output to be displayed in GitX
|
|
if (!isatty(STDIN_FILENO) && fdopen(STDIN_FILENO, "r"))
|
|
handleSTDINDiff();
|
|
|
|
// From this point, we require a working directory and the arguments
|
|
NSMutableArray *arguments = argumentsArray();
|
|
NSURL *wdURL = workingDirectoryURL(arguments);
|
|
|
|
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);
|
|
}
|
|
|
|
if (searchModeForCommandLineArgument(firstArgument)) {
|
|
handleGitXSearch(wdURL, arguments);
|
|
}
|
|
}
|
|
|
|
// No commands handled by gitx, open the current dir in GitX with the arguments
|
|
handleOpenRepository(wdURL, arguments);
|
|
|
|
return 0;
|
|
}
|