Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

384 lines (314 sloc) 13.163 kB
//
// 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, NSMutableArray *arguments)
{
[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);
}
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);
}
}
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;
}
Jump to Line
Something went wrong with that request. Please try again.