diff --git a/PBGitBinary.m b/PBGitBinary.m index 49ef7b7..ccb0b0b 100644 --- a/PBGitBinary.m +++ b/PBGitBinary.m @@ -66,7 +66,7 @@ static NSString* gitPath = nil; // Try to find the path of the Git binary char* path = getenv("GIT_PATH"); - if (path && [self acceptBinary:[NSString stringWithCString:path]]) + if (path && [self acceptBinary:[NSString stringWithUTF8String:path]]) return; // No explicit path. Try it with "which" diff --git a/PBGitCommitController.m b/PBGitCommitController.m index cec4ffc..8342fce 100644 --- a/PBGitCommitController.m +++ b/PBGitCommitController.m @@ -201,7 +201,6 @@ - (void) readOtherFiles:(NSNotification *)notification; { - [unstagedFilesController setAutomaticallyRearrangesObjects:NO]; NSArray *lines = [self linesFromNotification:notification]; NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:[lines count]]; // We fake this files status as good as possible. @@ -324,6 +323,11 @@ - (IBAction) commit:(id) sender { + if ([[NSFileManager defaultManager] fileExistsAtPath:[repository.fileURL.path stringByAppendingPathComponent:@"MERGE_HEAD"]]) { + [[repository windowController] showMessageSheet:@"Cannot commit merges" infoText:@"GitX cannot commit merges yet. Please commit your changes from the command line."]; + return; + } + if ([[cachedFilesController arrangedObjects] count] == 0) { [[repository windowController] showMessageSheet:@"No changes to commit" infoText:@"You must first stage some changes before committing"]; return; diff --git a/PBGitConfig.m b/PBGitConfig.m index 3d4f156..9166cc4 100644 --- a/PBGitConfig.m +++ b/PBGitConfig.m @@ -79,7 +79,7 @@ // Check if it exists globally. In that case, write it as a global - NSArray *arguments = [NSArray arrayWithObjects:@"config", @"--global", @"--get", path]; + NSArray *arguments = [NSArray arrayWithObjects:@"config", @"--global", @"--get", path, nil]; int ret; [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:arguments inDir:nil retValue:&ret]; if (!ret) // It exists globally diff --git a/PBGitHistoryController.h b/PBGitHistoryController.h index f87c462..6c99a20 100644 --- a/PBGitHistoryController.h +++ b/PBGitHistoryController.h @@ -42,6 +42,13 @@ - (IBAction) openSelectedFile: sender; - (void) updateQuicklookForce: (BOOL) force; +// Context menu methods +- (NSMenu *)contextMenuForTreeView; +- (NSArray *)menuItemsForPaths:(NSArray *)paths; +- (void)showCommitsFromTree:(id)sender; +- (void)showInFinderAction:(id)sender; +- (void)openFilesAction:(id)sender; + - (void) copyCommitInfo; - (BOOL) hasNonlinearPath; diff --git a/PBGitHistoryController.m b/PBGitHistoryController.m index 3b5e9e1..fb4d886 100644 --- a/PBGitHistoryController.m +++ b/PBGitHistoryController.m @@ -208,6 +208,7 @@ [super removeView]; } +#pragma mark Table Column Methods - (NSMenu *)tableColumnMenu { NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Table columns menu"]; @@ -223,4 +224,76 @@ return menu; } +#pragma mark Tree Context Menu Methods + +- (void)showCommitsFromTree:(id)sender +{ + // TODO: Enable this from webview as well! + + NSMutableArray *filePaths = [NSMutableArray arrayWithObjects:@"HEAD", @"--", NULL]; + [filePaths addObjectsFromArray:[sender representedObject]]; + + PBGitRevSpecifier *revSpec = [[PBGitRevSpecifier alloc] initWithParameters:filePaths]; + + repository.currentBranch = [repository addBranch:revSpec]; +} + +- (void)showInFinderAction:(id)sender +{ + NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"]; + NSString *path; + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + + for (NSString *filePath in [sender representedObject]) { + path = [workingDirectory stringByAppendingPathComponent:filePath]; + [ws selectFile: path inFileViewerRootedAtPath:path]; + } + +} + +- (void)openFilesAction:(id)sender +{ + NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"]; + NSString *path; + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + + for (NSString *filePath in [sender representedObject]) { + path = [workingDirectory stringByAppendingPathComponent:filePath]; + [ws openFile:path]; + } +} + + +- (NSMenu *)contextMenuForTreeView +{ + NSArray *filePaths = [[treeController selectedObjects] valueForKey:@"fullPath"]; + + NSMenu *menu = [[NSMenu alloc] init]; + for (NSMenuItem *item in [self menuItemsForPaths:filePaths]) + [menu addItem:item]; + return menu; +} + +- (NSArray *)menuItemsForPaths:(NSArray *)paths +{ + BOOL multiple = [paths count] != 1; + NSMenuItem *historyItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Show history of files" : @"Show history of file" + action:@selector(showCommitsFromTree:) + keyEquivalent:@""]; + NSMenuItem *finderItem = [[NSMenuItem alloc] initWithTitle:@"Show in Finder" + action:@selector(showInFinderAction:) + keyEquivalent:@""]; + NSMenuItem *openFilesItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Open Files" : @"Open File" + action:@selector(openFilesAction:) + keyEquivalent:@""]; + + NSArray *menuItems = [NSArray arrayWithObjects:historyItem, finderItem, openFilesItem, nil]; + for (NSMenuItem *item in menuItems) { + [item setTarget:self]; + [item setRepresentedObject:paths]; + } + + return menuItems; +} + @end diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib index 9861c21..3b7152f 100644 --- a/PBGitHistoryView.xib +++ b/PBGitHistoryView.xib @@ -9,7 +9,7 @@ YES - + YES @@ -2953,7 +2953,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{178, 79}, {852, 432}} + {{358, 67}, {852, 432}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2988,7 +2988,7 @@ - 274 + 286 @@ -3028,6 +3028,8 @@ setDetailedView: setRawView: setTreeView: + showCommitsFromTree: + showInFinderAction: toggleQuickView: @@ -3038,6 +3040,8 @@ id id id + id + id @@ -3048,6 +3052,7 @@ commitList fileBrowser searchField + treeContextMenu treeController webView @@ -3057,6 +3062,7 @@ NSTableView NSOutlineView NSSearchField + NSMenu NSTreeController id diff --git a/PBGitIndexController.m b/PBGitIndexController.m index 382f48f..4d3cc78 100644 --- a/PBGitIndexController.m +++ b/PBGitIndexController.m @@ -157,8 +157,18 @@ return [commitController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-files", [self contextParameter], @"--", file.path, nil]]; } -- (void) forceRevertChangesForFiles:(NSArray *)files +- (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force { + if(!force) { + int ret = [[NSAlert alertWithMessageText:@"Discard changes" + defaultButton:nil + alternateButton:@"Cancel" + otherButton:nil + informativeTextWithFormat:@"Are you sure you wish to discard the changes to this file?\n\nYou cannot undo this operation."] runModal]; + if (ret != NSAlertDefaultReturn) + return; + } + NSArray *paths = [files valueForKey:@"path"]; NSString *input = [paths componentsJoinedByString:@"\0"]; @@ -166,7 +176,7 @@ int ret = 1; [commitController.repository outputForArguments:arguments inputString:input retValue:&ret]; if (ret) { - [[commitController.repository windowController] showMessageSheet:@"Reverting changes failed" infoText:[NSString stringWithFormat:@"Reverting changes failed with error code %i", ret]]; + [[commitController.repository windowController] showMessageSheet:@"Discarding changes failed" infoText:[NSString stringWithFormat:@"Discarding changes failed with error code %i", ret]]; return; } @@ -174,19 +184,6 @@ file.hasUnstagedChanges = NO; } -- (void) revertChangesForFiles:(NSArray *)files -{ - int ret = [[NSAlert alertWithMessageText:@"Revert changes" - defaultButton:nil - alternateButton:@"Cancel" - otherButton:nil - informativeTextWithFormat:@"Are you sure you wish to revert changes?\n\nYou cannot undo this operation."] runModal]; - - if (ret == NSAlertDefaultReturn) - [self forceRevertChangesForFiles:files]; -} - - # pragma mark Context Menu methods - (BOOL) allSelectedCanBeIgnored:(NSArray *)selectedFiles { @@ -244,19 +241,19 @@ if (!file.hasUnstagedChanges) return menu; - NSMenuItem *revertItem = [[NSMenuItem alloc] initWithTitle:@"Revert Changes…" action:@selector(revertFilesAction:) keyEquivalent:@""]; - [revertItem setTarget:self]; - [revertItem setAlternate:NO]; - [revertItem setRepresentedObject:selectedFiles]; + NSMenuItem *discardItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes" action:@selector(discardFilesAction:) keyEquivalent:@""]; + [discardItem setTarget:self]; + [discardItem setAlternate:NO]; + [discardItem setRepresentedObject:selectedFiles]; - [menu addItem:revertItem]; + [menu addItem:discardItem]; - NSMenuItem *revertForceItem = [[NSMenuItem alloc] initWithTitle:@"Revert Changes" action:@selector(forceRevertFilesAction:) keyEquivalent:@""]; - [revertForceItem setTarget:self]; - [revertForceItem setAlternate:YES]; - [revertForceItem setRepresentedObject:selectedFiles]; - [revertForceItem setKeyEquivalentModifierMask:NSAlternateKeyMask]; - [menu addItem:revertForceItem]; + NSMenuItem *discardForceItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes" action:@selector(forceDiscardFilesAction:) keyEquivalent:@""]; + [discardForceItem setTarget:self]; + [discardForceItem setAlternate:YES]; + [discardForceItem setRepresentedObject:selectedFiles]; + [discardForceItem setKeyEquivalentModifierMask:NSAlternateKeyMask]; + [menu addItem:discardForceItem]; return menu; } @@ -288,18 +285,18 @@ [commitController refresh:NULL]; } -- (void) revertFilesAction:(id) sender +- (void)discardFilesAction:(id) sender { NSArray *selectedFiles = [sender representedObject]; if ([selectedFiles count] > 0) - [self revertChangesForFiles:selectedFiles]; + [self discardChangesForFiles:selectedFiles force:FALSE]; } -- (void) forceRevertFilesAction:(id) sender +- (void)forceDiscardFilesAction:(id) sender { NSArray *selectedFiles = [sender representedObject]; if ([selectedFiles count] > 0) - [self forceRevertChangesForFiles:selectedFiles]; + [self discardChangesForFiles:selectedFiles force:TRUE]; } - (void) showInFinderAction:(id) sender diff --git a/PBGitRepository.m b/PBGitRepository.m index 89691f9..821cd2a 100644 --- a/PBGitRepository.m +++ b/PBGitRepository.m @@ -72,10 +72,12 @@ NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain"; return repositoryURL; } -- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError +// NSFileWrapper is broken and doesn't work when called on a directory containing a large number of directories and files. +//because of this it is safer to implement readFromURL than readFromFileWrapper. +//Because NSFileManager does not attempt to recursively open all directories and file when fileExistsAtPath is called +//this works much better. +- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError { - BOOL success = NO; - if (![PBGitBinary path]) { if (outError) { @@ -86,30 +88,32 @@ NSString* PBGitRepositoryErrorDomain = @"GitXErrorDomain"; return NO; } - if (![fileWrapper isDirectory]) { + BOOL isDirectory = FALSE; + [[NSFileManager defaultManager] fileExistsAtPath:[absoluteURL path] isDirectory:&isDirectory]; + if (!isDirectory) { if (outError) { - NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Reading files is not supported.", [fileWrapper filename]] - forKey:NSLocalizedRecoverySuggestionErrorKey]; + NSDictionary* userInfo = [NSDictionary dictionaryWithObject:@"Reading files is not supported." + forKey:NSLocalizedRecoverySuggestionErrorKey]; *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo]; } - } else { - NSURL* gitDirURL = [PBGitRepository gitDirForURL:[self fileURL]]; - if (gitDirURL) { - [self setFileURL:gitDirURL]; - success = YES; - } else if (outError) { - NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ does not appear to be a git repository.", [fileWrapper filename]] - forKey:NSLocalizedRecoverySuggestionErrorKey]; - *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo]; - } - - if (success) { - [self setup]; - [self readCurrentBranch]; - } + return NO; } - return success; + + NSURL* gitDirURL = [PBGitRepository gitDirForURL:[self fileURL]]; + if (!gitDirURL) { + if (outError) { + NSDictionary* userInfo = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ does not appear to be a git repository.", [self fileName]] + forKey:NSLocalizedRecoverySuggestionErrorKey]; + *outError = [NSError errorWithDomain:PBGitRepositoryErrorDomain code:0 userInfo:userInfo]; + } + return NO; + } + + [self setFileURL:gitDirURL]; + [self setup]; + [self readCurrentBranch]; + return YES; } - (void) setup diff --git a/PBQLOutlineView.m b/PBQLOutlineView.m index 4e620bd..459d908 100644 --- a/PBQLOutlineView.m +++ b/PBQLOutlineView.m @@ -58,6 +58,25 @@ return fileNames; } +- (NSMenu *)menuForEvent:(NSEvent *)theEvent +{ + if ([theEvent type] == NSRightMouseDown) + { + // get the current selections for the outline view. + NSIndexSet *selectedRowIndexes = [self selectedRowIndexes]; + + // select the row that was clicked before showing the menu for the event + NSPoint mousePoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; + int row = [self rowAtPoint:mousePoint]; + + // figure out if the row that was just clicked on is currently selected + if ([selectedRowIndexes containsIndex:row] == NO) + [self selectRow:row byExtendingSelection:NO]; + } + + return [controller contextMenuForTreeView]; +} + /* Implemented to satisfy datasourcee protocol */ - (BOOL) outlineView: (NSOutlineView *)ov isItemExpandable: (id)item { return NO; } diff --git a/PBWebHistoryController.m b/PBWebHistoryController.m index 0f553b3..35979a4 100644 --- a/PBWebHistoryController.m +++ b/PBWebHistoryController.m @@ -76,21 +76,23 @@ contextMenuItemsForElement:(NSDictionary *)element { DOMNode *node = [element valueForKey:@"WebElementDOMNode"]; - // If clicked on the text, select the containing div - if ([[node className] isEqualToString:@"DOMText"]) + while (node) { + // Every ref has a class name of 'refs' and some other class. We check on that to see if we pressed on a ref. + if ([[node className] hasPrefix:@"refs "]) { + NSString *selectedRefString = [[[node childNodes] item:0] textContent]; + for (PBGitRef *ref in historyController.webCommit.refs) + { + if ([[ref shortName] isEqualToString:selectedRefString]) + return [contextMenuDelegate menuItemsForRef:ref commit:historyController.webCommit]; + } + NSLog(@"Could not find selected ref!"); + return defaultMenuItems; + } + if ([node hasAttributes] && [[node attributes] getNamedItem:@"representedFile"]) + return [historyController menuItemsForPaths:[NSArray arrayWithObject:[[[node attributes] getNamedItem:@"representedFile"] value]]]; + node = [node parentNode]; - - // Every ref has a class name of 'refs' and some other class. We check on that to see if we pressed on a ref. - if (![[node className] hasPrefix:@"refs "]) - return defaultMenuItems; - - NSString *selectedRefString = [[[node childNodes] item:0] textContent]; - for (PBGitRef *ref in historyController.webCommit.refs) - { - if ([[ref shortName] isEqualToString:selectedRefString]) - return [contextMenuDelegate menuItemsForRef:ref commit:historyController.webCommit]; } - NSLog(@"Could not find selected ref!"); return defaultMenuItems; } diff --git a/Site/templates/site.html b/Site/templates/site.html index c1001b6..c308843 100644 --- a/Site/templates/site.html +++ b/Site/templates/site.html @@ -40,7 +40,7 @@ end <%= @body %>