Files
gitx/PBRefController.m
T
André Berg ff550e85f1 More cleanup and feature adding for the Push, Pull and Rebase popup button toolbar items.
- Complete the action methods for fetch and rebase. 
- Add a more generic and re-usable method for updating the popup toolbar items.
- Comment out updatePushMenuWithRemotes: and updatePullMenuWithRemotes: as those have been 
  superseded by updatePopUpToolbarItemMenu:remotes:action:title:
2009-11-10 12:41:02 +01:00

883 lines
29 KiB
Objective-C

//
// PBLabelController.m
// GitX
//
// Created by Pieter de Bie on 21-10-08.
// Copyright 2008 Pieter de Bie. All rights reserved.
//
#import "PBRefController.h"
#import "PBGitRevisionCell.h"
#import "PBRefMenuItem.h"
#import "KBPopUpToolbarItem.h"
@implementation PBRefController
- (void)awakeFromNib
{
[commitList registerForDraggedTypes:[NSArray arrayWithObject:@"PBGitRef"]];
[historyController addObserver:self forKeyPath:@"repository.branches" options:0 context:@"branchChange"];
[historyController addObserver:self forKeyPath:@"repository.currentBranch" options:0 context:@"currentBranchChange"];
[self updateBranchMenus];
[self selectCurrentBranch];
}
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([(NSString *)context isEqualToString: @"branchChange"]) {
[self updateBranchMenus];
}
else if ([(NSString *)context isEqualToString:@"currentBranchChange"]) {
[self selectCurrentBranch];
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void) removeRefSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
if (returnCode == NSAlertDefaultReturn) {
int ret = 1;
PBRefMenuItem *refMenuItem = contextInfo;
[historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-d", [[refMenuItem ref] ref], nil] retValue: &ret];
if (ret) {
NSLog(@"Removing ref failed!");
return;
}
[historyController.repository removeBranch:[[PBGitRevSpecifier alloc] initWithRef:[refMenuItem ref]]];
[[refMenuItem commit] removeRef:[refMenuItem ref]];
[commitController rearrangeObjects];
[self updateBranchMenus];
}
}
- (void) removeRef:(PBRefMenuItem *)sender
{
NSString *ref_desc = [NSString stringWithFormat:@"%@ %@", [[sender ref] type], [[sender ref] shortName]];
NSString *question = [NSString stringWithFormat:@"Are you sure you want to remove the %@?", ref_desc];
NSBeginAlertSheet([NSString stringWithFormat:@"Delete %@?", ref_desc], @"Delete", @"Cancel", nil, [[historyController view] window], self, @selector(removeRefSheetDidEnd:returnCode:contextInfo:), NULL, sender, question);
}
- (void) checkoutRef:(PBRefMenuItem *)sender
{
int ret = 1;
[historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"checkout", [[sender ref] shortName], nil] retValue: &ret];
if (ret) {
NSString *info = [NSString stringWithFormat:@"There was an error checking out the branch. Perhaps your working directory is not clean?"];
[[historyController.repository windowController] showMessageSheet:@"Checking out branch failed" infoText:info];
return;
}
[historyController.repository reloadRefs];
[historyController.repository readCurrentBranch];
[commitController rearrangeObjects];
}
- (void) pushRef:(PBRefMenuItem *)sender
{
[self pushImpl:[[sender ref] shortName]];
}
- (void) pullRef:(PBRefMenuItem *)sender
{
[self pullImpl:[[sender ref] shortName]];
}
- (void) rebaseRef:(PBRefMenuItem *)sender
{
[self rebaseImpl:[[sender ref] shortName]];
}
- (BOOL) fetchRef:(PBRefMenuItem *)sender
{
[self fetchImpl:[[sender ref] shortName]];
}
- (BOOL) pushRef:(NSString *)refName toRemote:(NSString *)remote
{
int ret = 1;
BOOL success = NO;
NSString *rval = [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"push", remote, refName, nil] retValue: &ret];
if (!remote) {
[self showMessageSheet:@"Push to Remote" message:PBMissingRemoteErrorMessage];
return success;
}
if (ret) {
NSString *info = [NSString stringWithFormat:@"There was an error pushing the branch to the remote repository.\n\n%d\n%@", ret, rval];
[[historyController.repository windowController] showMessageSheet:@"Pushing branch failed" infoText:info];
return success;
}
[historyController.repository reloadRefs];
[commitController rearrangeObjects];
success = YES;
return success;
}
- (BOOL) pullRef:(NSString *)refName fromRemote:(NSString *)remote
{
int ret = 1;
BOOL success = NO;
NSArray * args = [NSArray arrayWithObjects:@"pull", remote, refName, nil];
NSString *rval = [historyController.repository outputInWorkdirForArguments:args retValue: &ret];
if (ret) {
NSString *info = [NSString stringWithFormat:@"There was an error pulling from the remote repository.\n\n%d\n%@", ret, rval];
[[historyController.repository windowController] showMessageSheet:@"Pulling from remote failed" infoText:info];
return success;
}
[historyController.repository reloadRefs];
[commitController rearrangeObjects];
success = YES;
return success;
}
- (BOOL) rebaseRef:(NSString *)refName fromRemote:(NSString *)remote
{
int ret = 1;
BOOL success = NO;
NSArray * args = [NSArray arrayWithObjects:@"pull", @"--rebase", remote, refName, nil];
NSString *rval = [historyController.repository outputInWorkdirForArguments:args retValue: &ret];
if (ret) {
NSString *info = [NSString stringWithFormat:@"There was an error pulling from the remote repository.\n\n%d\n%@", ret, rval];
[[historyController.repository windowController] showMessageSheet:@"Pulling from remote failed" infoText:info];
return success;
}
[historyController.repository reloadRefs];
[commitController rearrangeObjects];
success = YES;
return success;
}
- (BOOL) fetchRef:(NSString *)refName fromRemote:(NSString *)remote
{
int ret = 1;
BOOL success = NO;
NSArray * args = [NSArray arrayWithObjects:@"pull", @"--rebase", remote, refName, nil];
NSString *rval = [historyController.repository outputInWorkdirForArguments:args retValue: &ret];
if (ret) {
NSString *info = [NSString stringWithFormat:@"There was an error pulling from the remote repository.\n\n%d\n%@", ret, rval];
[[historyController.repository windowController] showMessageSheet:@"Pulling from remote failed" infoText:info];
return success;
}
[historyController.repository reloadRefs];
[commitController rearrangeObjects];
success = YES;
return success;
}
- (BOOL) pushImpl:(NSString *)refName
{
NSString *remote = [[historyController.repository config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]];
if (!remote) {
[self showMessageSheet:@"Push to Remote" message:PBMissingRemoteErrorMessage];
return NO;
}
return [self pushRef:refName toRemote:remote];
}
- (BOOL) pullImpl:(NSString *)refName
{
NSString *remote = [[historyController.repository config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]];
if (!remote) {
[self showMessageSheet:@"Pull from Remote" message:PBMissingRemoteErrorMessage];
return NO;
}
return [self pullRef:refName fromRemote:remote];
}
- (BOOL) rebaseImpl:(NSString *)refName
{
NSString *remote = [[[historyController repository] config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]];
if (!remote) {
[self showMessageSheet:@"Pull from Remote and Rebase" message:PBMissingRemoteErrorMessage];
return NO;
}
return [self rebaseRef:refName fromRemote:remote];
}
- (BOOL) fetchImpl:(NSString *)refName
{
NSString *remote = [[historyController.repository config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]];
if (!remote) {
[self showMessageSheet:@"Fetch from Remote" message:PBMissingRemoteErrorMessage];
return NO;
}
return [self fetchRef:refName fromRemote:remote];
}
- (void) tagInfo:(PBRefMenuItem *)sender
{
NSString *message = [NSString stringWithFormat:@"Info for tag: %@", [[sender ref] shortName]];
int ret = 1;
NSString *info = [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"tag", @"-n50", @"-l", [[sender ref] shortName], nil] retValue: &ret];
if (!ret) {
[[historyController.repository windowController] showMessageSheet:message infoText:info];
}
return;
}
- (NSArray *) menuItemsForRef:(PBGitRef *)ref commit:(PBGitCommit *)commit
{
return [PBRefMenuItem defaultMenuItemsForRef:ref commit:commit target:self];
}
- (BOOL) addRemoteImplWithName:(NSString *)remoteName forURL:(NSString *)remoteURL
{
int ret = 1;
BOOL success = NO;
if (!remoteName || !remoteURL) {
return success;
}
NSString *rval = [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"remote", @"add", @"-f", remoteName, remoteURL, nil] retValue: &ret];
if (ret) {
NSString *info = [NSString stringWithFormat:@"There was an error adding the remote.\n\n%d\n%@", ret, rval];
[[historyController.repository windowController] showMessageSheet:@"Adding Remote failed" infoText:info];
return success;
}
[historyController.repository reloadRefs];
[commitController rearrangeObjects];
success = YES;
return success;
}
- (void) toggleToolbarItems:(NSToolbar *)tb matchingLabels:(NSArray *)labels enabledState:(BOOL)state {
NSArray * tbItems = [tb items];
/* if labels is nil, assume all toolbar items */
if (!labels) {
for (NSToolbarItem * curItem in tbItems) {
[curItem setEnabled:state];
}
} else {
for (NSToolbarItem * curItem in tbItems) {
for (NSString * curLabel in labels) {
if ([[curItem label] isEqualToString:curLabel]) {
[curItem setEnabled:state];
}
}
}
}
}
# pragma mark Tableview delegate methods
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
{
NSPoint location = [tv convertPointFromBase:[(PBCommitList *)tv mouseDownPoint]];
int row = [tv rowAtPoint:location];
int column = [tv columnAtPoint:location];
if (column != 0)
return NO;
PBGitRevisionCell *cell = (PBGitRevisionCell *)[tv preparedCellAtColumn:column row:row];
int index = [cell indexAtX:location.x];
if (index == -1)
return NO;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:row], [NSNumber numberWithInt:index], NULL]];
[pboard declareTypes:[NSArray arrayWithObject:@"PBGitRef"] owner:self];
[pboard setData:data forType:@"PBGitRef"];
return YES;
}
- (NSDragOperation)tableView:(NSTableView*)tv
validateDrop:(id <NSDraggingInfo>)info
proposedRow:(NSInteger)row
proposedDropOperation:(NSTableViewDropOperation)operation
{
if (operation == NSTableViewDropAbove)
return NSDragOperationNone;
NSPasteboard *pboard = [info draggingPasteboard];
if ([pboard dataForType:@"PBGitRef"])
return NSDragOperationMove;
return NSDragOperationNone;
}
- (BOOL)tableView:(NSTableView *)aTableView
acceptDrop:(id <NSDraggingInfo>)info
row:(NSInteger)row
dropOperation:(NSTableViewDropOperation)operation
{
if (operation != NSTableViewDropOn)
return NO;
NSPasteboard *pboard = [info draggingPasteboard];
NSData *data = [pboard dataForType:@"PBGitRef"];
if (!data)
return NO;
NSArray *numbers = [NSKeyedUnarchiver unarchiveObjectWithData:data];
int oldRow = [[numbers objectAtIndex:0] intValue];
int oldRefIndex = [[numbers objectAtIndex:1] intValue];
PBGitCommit *oldCommit = [[commitController arrangedObjects] objectAtIndex: oldRow];
PBGitRef *ref = [[oldCommit refs] objectAtIndex:oldRefIndex];
PBGitCommit *dropCommit = [[commitController arrangedObjects] objectAtIndex:row];
int a = [[NSAlert alertWithMessageText:@"Change branch"
defaultButton:@"Change"
alternateButton:@"Cancel"
otherButton:nil
informativeTextWithFormat:@"Do you want to change branch\n\n\t'%@'\n\n to point to commit\n\n\t'%@'", [ref shortName], [dropCommit subject]] runModal];
if (a != NSAlertDefaultReturn)
return NO;
int retValue = 1;
[historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mUpdate from GitX", [ref ref], [dropCommit realSha], NULL] retValue:&retValue];
if (retValue)
return NO;
[dropCommit addRef:ref];
[oldCommit removeRef:ref];
[commitController rearrangeObjects];
[aTableView needsToDrawRect:[aTableView rectOfRow:oldRow]];
return YES;
}
# pragma mark Add ref methods
-(void)addRef:(id)sender
{
[errorMessage setStringValue:@""];
[NSApp beginSheet:newBranchSheet
modalForWindow:[[historyController view] window]
modalDelegate:NULL
didEndSelector:NULL
contextInfo:NULL];
}
// MARK: Buttons
-(void)rebaseButton:(id)sender
{
NSString *refName = [[[[historyController repository] currentBranch] simpleRef] refForSpec];
if (refName) {
[self rebaseImpl:refName];
} else {
[self showMessageSheet:@"Pull Rebase from Remote" message:PBInvalidBranchErrorMessage];
}
// NSLog([NSString stringWithFormat:@"Rebase hit for %@!", refName]);
}
-(void)pushButton:(id)sender
{
NSString *refName = [[[[historyController repository] currentBranch] simpleRef] refForSpec];
if (refName) {
[self pushImpl:refName];
} else {
[self showMessageSheet:@"Push to Remote" message:PBInvalidBranchErrorMessage];
}
// NSLog([NSString stringWithFormat:@"Push hit for %@!", refName]);
}
- (void) pullButton:(id)sender
{
NSString * refName = [[[[historyController repository] currentBranch] simpleRef] refForSpec];
if (refName) {
[self pullImpl:refName];
} else {
[sender setEnabled:YES];
[self showMessageSheet:@"Pull from Remote" message:PBInvalidBranchErrorMessage];
}
// NSLog([NSString stringWithFormat:@"Pull hit for %@!", refName]);
}
-(void)fetchButton:(id)sender
{
NSString *refName = [[[[historyController repository] currentBranch] simpleRef] refForSpec];
if (refName) {
[self fetchImpl:refName];
} else {
[sender setEnabled:YES];
[self showMessageSheet:@"Fetch from Remote" message:PBInvalidBranchErrorMessage];
}
// NSLog([NSString stringWithFormat:@"Fetch hit for %@!", refName]);
}
// MARK: Sheets
- (void) showMessageSheet:(NSString *)title message:(NSString *)msg {
[[NSAlert alertWithMessageText:title
defaultButton:@"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:msg]
beginSheetModalForWindow:[[historyController view] window]
modalDelegate:self
didEndSelector:nil
contextInfo:nil];
return;
}
-(void)saveSheet:(id) sender
{
NSString *branchName = [@"refs/heads/" stringByAppendingString:[newBranchName stringValue]];
if ([[commitController selectedObjects] count] == 0)
return;
PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0];
int retValue = 1;
[historyController.repository outputForArguments:[NSArray arrayWithObjects:@"check-ref-format", branchName, nil] retValue:&retValue];
if (retValue != 0) {
[errorMessage setStringValue:@"Invalid name"];
return;
}
retValue = 1;
[historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mCreate branch from GitX", branchName, [commit realSha], @"0000000000000000000000000000000000000000", NULL] retValue:&retValue];
if (retValue)
{
[errorMessage setStringValue:@"Branch exists"];
return;
}
[historyController.repository addBranch:[[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:branchName]]];
[self closeSheet:sender];
[commit addRef:[PBGitRef refFromString:branchName]];
[commitController rearrangeObjects];
}
-(void)closeSheet:(id) sender
{
[NSApp endSheet:newBranchSheet];
[newBranchName setStringValue:@""];
[newBranchSheet orderOut:self];
}
- (void) addRemoteButton:(id)sender
{
[addRemoteErrorMessage setStringValue:@""];
[addRemoteName setStringValue:@""];
[addRemoteName setTextColor:[NSColor blackColor]];
[addRemoteURL setStringValue:@""];
[NSApp beginSheet:addRemoteSheet
modalForWindow:[[historyController view] window]
modalDelegate:NULL
didEndSelector:NULL
contextInfo:NULL];
}
- (void) addRemoteSheet:(id)sender
{
NSString *remoteName = [addRemoteName stringValue];
NSString *remoteURL = [addRemoteURL stringValue];
NSLog(@"%s remoteName = %@ remoteURL = %@", _cmd, remoteName, remoteURL);
if ([remoteName isEqualToString:@""]) {
[addRemoteErrorMessage setStringValue:@"Remote name is required"];
return;
}
NSRange range = [remoteName rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
if (range.location != NSNotFound) {
[addRemoteErrorMessage setStringValue:@"Whitespace is not allowed"];
[addRemoteName setTextColor:[NSColor redColor]];
return;
}
[addRemoteName setTextColor:[NSColor blackColor]];
if ([remoteURL isEqualToString:@""]) {
[addRemoteErrorMessage setStringValue:@"Remote URL is required"];
return;
}
[addRemoteURL setTextColor:[NSColor blackColor]];
[self closeAddRemoteSheet:sender];
[self addRemoteImplWithName:remoteName forURL:remoteURL];
}
- (void) closeAddRemoteSheet:(id)sender
{
[NSApp endSheet:addRemoteSheet];
[addRemoteErrorMessage setStringValue:@""];
[addRemoteName setStringValue:@""];
[addRemoteName setTextColor:[NSColor blackColor]];
[addRemoteURL setStringValue:@""];
[addRemoteSheet orderOut:self];
}
- (void) newTagButton:(id)sender
{
[newTagErrorMessage setStringValue:@""];
[newTagName setStringValue:@""];
if ([[commitController selectedObjects] count] != 0) {
PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0];
[newTagCommit setStringValue:[commit subject]];
[newTagSHA setStringValue:[commit realSha]];
[newTagSHALabel setHidden:NO];
} else {
[newTagCommit setStringValue:historyController.repository.currentBranch.description];
[newTagSHA setStringValue:@""];
[newTagSHALabel setHidden:YES];
}
[NSApp beginSheet:newTagSheet
modalForWindow:[[historyController view] window]
modalDelegate:NULL
didEndSelector:NULL
contextInfo:NULL];
}
- (void) newTagSheet:(id)sender
{
NSString *tagName = [newTagName stringValue];
if ([tagName isEqualToString:@""]) {
[newTagErrorMessage setStringValue:@"Invalid name"];
return;
}
PBGitCommit *commit = nil;
if ([[commitController selectedObjects] count] != 0)
commit = [[commitController selectedObjects] objectAtIndex:0];
NSString *refName = [@"refs/tags/" stringByAppendingString:tagName];
int retValue = 1;
[historyController.repository outputForArguments:[NSArray arrayWithObjects:@"check-ref-format", refName, nil] retValue:&retValue];
if (retValue != 0) {
[newTagErrorMessage setStringValue:@"Invalid name"];
return;
}
NSString *message = [newTagMessage string];
NSMutableArray *arguments = [NSMutableArray arrayWithObject:@"tag"];
if (![message isEqualToString:@""]) {
[arguments addObject:[@"-m" stringByAppendingString:message]];
}
[arguments addObject:tagName];
if (commit) {
[arguments addObject:[commit realSha]];
}
NSLog(@"arguments = %@", arguments);
retValue = 1;
[historyController.repository outputForArguments:arguments retValue:&retValue];
if (retValue)
{
[newTagErrorMessage setStringValue:@"Tag exists"];
return;
}
[self closeNewTagSheet:sender];
[historyController.repository reloadRefs];
[commitController rearrangeObjects];
}
- (void) closeNewTagSheet:(id)sender
{
[NSApp endSheet:newTagSheet];
[newTagErrorMessage setStringValue:@""];
[newTagName setStringValue:@""];
[newTagSheet orderOut:self];
}
# pragma mark Branch menus
- (void) updateAllBranchesMenuWithLocal:(NSMutableArray *)localBranches remote:(NSMutableArray *)remoteBranches tag:(NSMutableArray *)tags other:(NSMutableArray *)other
{
if (!branchPopUp)
return;
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Branch menu"];
// Local
for (PBGitRevSpecifier *rev in localBranches)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
[item setRepresentedObject:rev];
[item setTarget:self];
[menu addItem:item];
}
[menu addItem:[NSMenuItem separatorItem]];
// Remotes
NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Remotes"];
NSMenu *currentMenu = nil;
for (PBGitRevSpecifier *rev in remoteBranches)
{
NSString *ref = [rev simpleRef];
NSArray *components = [ref componentsSeparatedByString:@"/"];
NSString *remoteName = [components objectAtIndex:2];
NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"];
if (![[currentMenu title] isEqualToString:remoteName])
{
currentMenu = [[NSMenu alloc] initWithTitle:remoteName];
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""];
[item setSubmenu:currentMenu];
[remoteMenu addItem:item];
}
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(changeBranch:) keyEquivalent:@""];
[item setTarget:self];
[item setRepresentedObject:rev];
[currentMenu addItem:item];
}
NSMenuItem *remoteItem = [[NSMenuItem alloc] initWithTitle:@"Remotes" action:NULL keyEquivalent:@""];
[remoteItem setSubmenu:remoteMenu];
[menu addItem:remoteItem];
// Tags
NSMenu *tagMenu = [[NSMenu alloc] initWithTitle:@"Tags"];
for (PBGitRevSpecifier *rev in tags)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
[item setTarget:self];
[item setRepresentedObject:rev];
[tagMenu addItem:item];
}
NSMenuItem *tagItem = [[NSMenuItem alloc] initWithTitle:@"Tags" action:NULL keyEquivalent:@""];
[tagItem setSubmenu:tagMenu];
[menu addItem:tagItem];
// Others
[menu addItem:[NSMenuItem separatorItem]];
for (PBGitRevSpecifier *rev in other)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
[item setRepresentedObject:rev];
[item setTarget:self];
[menu addItem:item];
}
[[branchPopUp cell] setMenu: menu];
}
- (void) updatePopUpToolbarItemMenu:(KBPopUpToolbarItem *)item remotes:(NSMutableArray *)remoteBranches action:(SEL)action title:(NSString *)menuTitle
{
if (!item)
return;
NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:menuTitle];
// Remotes
NSMenu *currentMenu = nil;
for (PBGitRevSpecifier *rev in remoteBranches)
{
NSString *ref = [rev simpleRef];
NSArray *components = [ref componentsSeparatedByString:@"/"];
NSString *remoteName = [components objectAtIndex:2];
NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"];
if (![[currentMenu title] isEqualToString:remoteName])
{
currentMenu = [[NSMenu alloc] initWithTitle:remoteName];
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""];
[item setSubmenu:currentMenu];
[remoteMenu addItem:item];
}
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:action keyEquivalent:@""];
[item setTarget:self];
[item setRepresentedObject:rev];
[currentMenu addItem:item];
}
[item setMenu: remoteMenu];
}
// !!! Andre Berg 20091110: The two methods below have been replaced with a more generic one (see above).
// Need to remove this at a later time...
/*
- (void) updatePullMenuWithRemotes:(NSMutableArray *)remoteBranches
{
if (!pullItem)
return;
NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Pull menu"];
// Remotes
NSMenu *currentMenu = nil;
for (PBGitRevSpecifier *rev in remoteBranches)
{
NSString *ref = [rev simpleRef];
NSArray *components = [ref componentsSeparatedByString:@"/"];
NSString *remoteName = [components objectAtIndex:2];
NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"];
if (![[currentMenu title] isEqualToString:remoteName])
{
currentMenu = [[NSMenu alloc] initWithTitle:remoteName];
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""];
[item setSubmenu:currentMenu];
[remoteMenu addItem:item];
}
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(pullMenuAction:) keyEquivalent:@""];
[item setTarget:self];
[item setRepresentedObject:rev];
[currentMenu addItem:item];
}
[pullItem setMenu: remoteMenu];
}
- (void) updatePushMenuWithRemotes:(NSMutableArray *)remoteBranches
{
if (!pushItem)
return;
NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Push menu"];
// Remotes
NSMenu *currentMenu = nil;
for (PBGitRevSpecifier *rev in remoteBranches)
{
NSString *ref = [rev simpleRef];
NSArray *components = [ref componentsSeparatedByString:@"/"];
NSString *remoteName = [components objectAtIndex:2];
NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"];
if (![[currentMenu title] isEqualToString:remoteName])
{
currentMenu = [[NSMenu alloc] initWithTitle:remoteName];
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""];
[item setSubmenu:currentMenu];
[remoteMenu addItem:item];
}
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(pushMenuAction:) keyEquivalent:@""];
[item setTarget:self];
[item setRepresentedObject:rev];
[currentMenu addItem:item];
}
[pushItem setMenu: remoteMenu];
}*/
- (void) updateBranchMenus
{
NSMutableArray *localBranches = [NSMutableArray array];
NSMutableArray *remoteBranches = [NSMutableArray array];
NSMutableArray *tags = [NSMutableArray array];
NSMutableArray *other = [NSMutableArray array];
for (PBGitRevSpecifier *rev in historyController.repository.branches)
{
if (![rev isSimpleRef])
{
[other addObject:rev];
continue;
}
NSString *ref = [rev simpleRef];
if ([ref hasPrefix:@"refs/heads"])
[localBranches addObject:rev];
else if ([ref hasPrefix:@"refs/tags"])
[tags addObject:rev];
else if ([ref hasPrefix:@"refs/remote"])
[remoteBranches addObject:rev];
}
[self updateAllBranchesMenuWithLocal:localBranches remote:remoteBranches tag:tags other:other];
[self updatePopUpToolbarItemMenu:pushItem remotes:remoteBranches action:@selector(pushMenuAction:) title:@"Push menu"];
[self updatePopUpToolbarItemMenu:pullItem remotes:remoteBranches action:@selector(pullMenuAction:) title:@"Push menu"];
[self updatePopUpToolbarItemMenu:rebaseItem remotes:remoteBranches action:@selector(rebaseMenuAction:) title:@"Push menu"];
// [self updatePullMenuWithRemotes:remoteBranches];
//
// [self updatePushMenuWithRemotes:remoteBranches];
}
- (void) changeBranch:(NSMenuItem *)sender
{
PBGitRevSpecifier *rev = [sender representedObject];
historyController.repository.currentBranch = rev;
}
- (void) selectCurrentBranch
{
PBGitRevSpecifier *rev = historyController.repository.currentBranch;
[branchPopUp setTitle:[rev description]];
// !!! Andre Berg 20091110: I don't think this is needed any more since the Push, Pull
// and Rebase toolbar items are now popup buttons it makes no sense to disable them
// when switching to "All branches" or "Local branches" since you can choose the remote
// from the popup menus.
// NSToolbar * tb = historyController.viewToolbar;
// NSArray * tbLabels = [NSArray arrayWithObjects:@"Push", @"Pull", @"Rebase", nil];
// if (rev) {
// [branchPopUp setTitle:[rev description]];
//
// if ([[rev description] isEqualToString:@"All branches"] ||
// [[rev description] isEqualToString:@"Local branches"])
// {
// [self toggleToolbarItems:tb matchingLabels:tbLabels enabledState:NO];
// } else {
// [self toggleToolbarItems:tb matchingLabels:tbLabels enabledState:YES];
// }
// } else {
// /* just in case, re-enable all toolbar buttons */
// [self toggleToolbarItems:tb matchingLabels:nil enabledState:YES];
// }
}
- (void) pullMenuAction:(NSMenuItem *)sender
{
NSString *ref = [(PBGitRevSpecifier *)[sender representedObject] description];
NSArray *refComponents = [ref componentsSeparatedByString:@"/"];
if ([refComponents count] != 2)
return;
[self pullRef:[refComponents objectAtIndex:1] fromRemote:[refComponents objectAtIndex:0]];
}
- (void) pushMenuAction:(NSMenuItem *)sender
{
NSString *ref = [(PBGitRevSpecifier *)[sender representedObject] description];
NSArray *refComponents = [ref componentsSeparatedByString:@"/"];
if ([refComponents count] != 2)
return;
[self pushRef:[refComponents objectAtIndex:1] toRemote:[refComponents objectAtIndex:0]];
}
- (void) rebaseMenuAction:(NSMenuItem *)sender
{
NSString *ref = [(PBGitRevSpecifier *)[sender representedObject] description];
NSArray *refComponents = [ref componentsSeparatedByString:@"/"];
if ([refComponents count] != 2)
return;
[self rebaseRef:[refComponents objectAtIndex:1] fromRemote:[refComponents objectAtIndex:0]];
}
@end
@implementation NSString (PBRefSpecAdditions)
/* convenience method to get the last part of a simple refspec like refs/heads/master -> master*/
- (NSString *) refForSpec {
if ([self hasPrefix:@"refs/"]) {
NSArray * parts = [self componentsSeparatedByString:@"/"];
return [parts lastObject];
}
return self;
}
@end