diff --git a/Classes/Controllers/PBGitCommitController.h b/Classes/Controllers/PBGitCommitController.h index aac727fc8..9d4a0bee9 100644 --- a/Classes/Controllers/PBGitCommitController.h +++ b/Classes/Controllers/PBGitCommitController.h @@ -16,11 +16,17 @@ // This might have to transfer over to the PBGitRepository // object sometime PBGitIndex *index; + + BOOL stashKeepIndex; IBOutlet NSTextView *commitMessageView; IBOutlet NSArrayController *unstagedFilesController; IBOutlet NSArrayController *cachedFilesController; + IBOutlet NSArrayController *trackedFilesController; + + IBOutlet NSTabView *controlsTabView; IBOutlet NSButton *commitButton; + IBOutlet NSButton *stashButton; IBOutlet PBGitIndexController *indexController; IBOutlet PBWebChangesController *webController; @@ -28,9 +34,15 @@ } @property(readonly) PBGitIndex *index; +@property(assign) BOOL stashKeepIndex; - (IBAction) refresh:(id) sender; - (IBAction) commit:(id) sender; - (IBAction) forceCommit:(id) sender; -- (IBAction)signOff:(id)sender; +- (IBAction) signOff:(id)sender; +- (IBAction) stashChanges:(id) sender; + +- (NSView *) nextKeyViewFor:(NSView *)view; +- (NSView *) previousKeyViewFor:(NSView *)view; + @end diff --git a/Classes/Controllers/PBGitCommitController.m b/Classes/Controllers/PBGitCommitController.m index d57f2c66b..dd9b643d6 100644 --- a/Classes/Controllers/PBGitCommitController.m +++ b/Classes/Controllers/PBGitCommitController.m @@ -13,13 +13,16 @@ #import "PBGitIndex.h" #import "PBNiceSplitView.h" #import "PBGitRepositoryWatcher.h" +#import "PBGitIndexController.h" #import #import #define kCommitSplitViewPositionDefault @"Commit SplitView Position" +#define kControlsTabIndexCommit 0 +#define kControlsTabIndexStash 1 -@interface PBGitCommitController () +@interface PBGitCommitController () - (void)refreshFinished:(NSNotification *)notification; - (void)commitWithVerification:(BOOL) doVerify; - (void)commitStatusUpdated:(NSNotification *)notification; @@ -35,6 +38,7 @@ - (void)saveCommitSplitViewPosition; @implementation PBGitCommitController @synthesize index; +@synthesize stashKeepIndex; - (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller { @@ -60,10 +64,12 @@ - (void)awakeFromNib { [super awakeFromNib]; + commitMessageView.delegate = self; [commitMessageView setTypingAttributes:[NSDictionary dictionaryWithObject:[NSFont fontWithName:@"Monaco" size:12.0] forKey:NSFontAttributeName]]; [unstagedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasUnstagedChanges == 1"]]; [cachedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasStagedChanges == 1"]]; + [trackedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"status > 0"]]; [unstagedFilesController setSortDescriptors:[NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey:@"status" ascending:false], @@ -123,6 +129,8 @@ - (IBAction)signOff:(id)sender - (void) refresh:(id) sender { + [controlsTabView selectTabViewItemAtIndex:kControlsTabIndexCommit]; + self.isBusy = YES; self.status = @"Refreshing index…"; [index refresh]; @@ -136,6 +144,12 @@ - (void) updateView [self refresh:nil]; } +- (IBAction) stashChanges:(id)sender +{ + NSLog(@"stash changes: %@", stashKeepIndex ? @"keep index" : @""); + [self.repository stashSaveWithKeepIndex:stashKeepIndex]; +} + - (IBAction) commit:(id) sender { [self commitWithVerification:YES]; @@ -226,12 +240,12 @@ - (void)indexChanged:(NSNotification *)notification { [cachedFilesController rearrangeObjects]; [unstagedFilesController rearrangeObjects]; - if ([[cachedFilesController arrangedObjects] count]) { - [commitButton setEnabled:YES]; - } else { - [commitButton setEnabled:NO]; - } - + + NSUInteger tracked = [[trackedFilesController arrangedObjects] count]; + NSUInteger staged = [[cachedFilesController arrangedObjects] count]; + + [commitButton setEnabled:(staged > 0)]; + [stashButton setEnabled:(staged > 0 || tracked > 0)]; } - (void)indexOperationFailed:(NSNotification *)notification @@ -313,4 +327,71 @@ - (void)restoreCommitSplitViewPositiion [commitSplitView setHidden:NO]; } +#pragma mark Handle "alt" key-down/up events +// to toggle commit/stash controls + +- (void)flagsChanged:(NSEvent *)theEvent +{ + BOOL altDown = !!([theEvent modifierFlags] & NSAlternateKeyMask); + int currIndex = [controlsTabView indexOfTabViewItem:controlsTabView.selectedTabViewItem]; + int desiredIndex = altDown ? kControlsTabIndexStash : kControlsTabIndexCommit; + if (currIndex != desiredIndex) { + [controlsTabView selectTabViewItemAtIndex:desiredIndex]; + } +} + + +#pragma mark NSTextView delegate methods + +- (void)focusTable:(NSTableView *)table +{ + if ([table numberOfRows] > 0) { + if ([table numberOfSelectedRows] == 0) { + [table selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + } + [[table window] makeFirstResponder:table]; + } +} + +- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector; +{ + if (commandSelector == @selector(insertTab:)) { + [self focusTable:indexController.stagedTable]; + return YES; + } else if (commandSelector == @selector(insertBacktab:)) { + [self focusTable:indexController.unstagedTable]; + return YES; + } + return NO; +} + +# pragma mark Key View Chain + +-(NSView *)nextKeyViewFor:(NSView *)view +{ + NSView * next = nil; + if (view == indexController.unstagedTable) { + next = commitMessageView; + } + else if (view == commitMessageView) { + next = indexController.stagedTable; + } + else if (view == indexController.stagedTable) { + next = commitButton; + } + return next; +} + +-(NSView *)previousKeyViewFor:(NSView *)view +{ + NSView * next = nil; + if (view == indexController.stagedTable) { + next = commitMessageView; + } + else if (view == commitMessageView) { + next = indexController.unstagedTable; + } + return next; +} + @end diff --git a/Classes/Controllers/PBGitIndexController.h b/Classes/Controllers/PBGitIndexController.h index 2e7277a1c..44b831a83 100644 --- a/Classes/Controllers/PBGitIndexController.h +++ b/Classes/Controllers/PBGitIndexController.h @@ -18,10 +18,16 @@ IBOutlet NSTableView *stagedTable; } +@property (readonly) NSTableView *unstagedTable; +@property (readonly) NSTableView *stagedTable; + - (IBAction) rowClicked:(NSCell *) sender; - (IBAction) tableClicked:(NSTableView *)tableView; - (NSMenu *) menuForTable:(NSTableView *)table; +- (NSView *) nextKeyViewFor:(NSView *)view; +- (NSView *) previousKeyViewFor:(NSView *)view; + - (void) stageSelectedFiles; - (void) unstageSelectedFiles; diff --git a/Classes/Controllers/PBGitIndexController.m b/Classes/Controllers/PBGitIndexController.m index ec7f575d9..666758651 100644 --- a/Classes/Controllers/PBGitIndexController.m +++ b/Classes/Controllers/PBGitIndexController.m @@ -19,6 +19,8 @@ - (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force; @implementation PBGitIndexController +@synthesize stagedTable, unstagedTable; + - (void)awakeFromNib { [unstagedTable setDoubleAction:@selector(tableClicked:)]; @@ -386,4 +388,16 @@ - (BOOL)tableView:(NSTableView *)aTableView return YES; } +# pragma mark Key View Chain + +-(NSView *)nextKeyViewFor:(NSView *)view +{ + return [commitController nextKeyViewFor:view]; +} + +-(NSView *)previousKeyViewFor:(NSView *)view +{ + return [commitController previousKeyViewFor:view]; +} + @end diff --git a/Classes/Controllers/PBGitSidebarController.h b/Classes/Controllers/PBGitSidebarController.h index e28936c33..f825ec87b 100644 --- a/Classes/Controllers/PBGitSidebarController.h +++ b/Classes/Controllers/PBGitSidebarController.h @@ -25,7 +25,7 @@ /* Specific things */ PBSourceViewItem *stage; - PBSourceViewItem *branches, *remotes, *tags, *others, *submodules; + PBSourceViewItem *branches, *remotes, *tags, *others, *submodules, *stashes; PBGitHistoryController *historyViewController; PBGitCommitController *commitViewController; diff --git a/Classes/Controllers/PBGitSidebarController.m b/Classes/Controllers/PBGitSidebarController.m index d7ed42b61..fd02740f7 100644 --- a/Classes/Controllers/PBGitSidebarController.m +++ b/Classes/Controllers/PBGitSidebarController.m @@ -16,6 +16,8 @@ #import "PBAddRemoteSheet.h" #import "PBGitDefaults.h" #import "PBHistorySearchController.h" +#import "PBGitStash.h" +#import "PBGitSVStashItem.h" @interface PBGitSidebarController () @@ -53,6 +55,7 @@ - (void)awakeFromNib [repository addObserver:self forKeyPath:@"currentBranch" options:0 context:@"currentBranchChange"]; [repository addObserver:self forKeyPath:@"branches" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:@"branchesModified"]; + [repository addObserver:self forKeyPath:@"stashes" options:0 context:@"stashesModified"]; [sourceView setTarget:self]; [sourceView setDoubleAction:@selector(doubleClicked:)]; @@ -82,6 +85,7 @@ - (void)closeView [repository removeObserver:self forKeyPath:@"currentBranch"]; [repository removeObserver:self forKeyPath:@"branches"]; + [repository removeObserver:self forKeyPath:@"stashes"]; [super closeView]; } @@ -112,6 +116,20 @@ - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:( } return; } + + if ([@"stashesModified" isEqualToString:(__bridge NSString*)context]) { + + for (PBGitSVStashItem *stashItem in stashes.sortedChildren) + [stashes removeChild:stashItem]; + + for (PBGitStash *stash in repository.stashes) + [stashes addChild: [PBGitSVStashItem itemWithStash:stash]]; + + [sourceView expandItem:stashes]; + [sourceView reloadItem:stashes reloadChildren:YES]; + + return; + } [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } @@ -288,9 +306,13 @@ - (void)populateList branches = [PBSourceViewItem groupItemWithTitle:@"Branches"]; remotes = [PBSourceViewItem groupItemWithTitle:@"Remotes"]; tags = [PBSourceViewItem groupItemWithTitle:@"Tags"]; + stashes = [PBSourceViewItem groupItemWithTitle:@"Stashes"]; submodules = [PBSourceViewItem groupItemWithTitle:@"Submodules"]; others = [PBSourceViewItem groupItemWithTitle:@"Other"]; + for (PBGitStash *stash in repository.stashes) + [stashes addChild: [PBGitSVStashItem itemWithStash:stash]]; + for (PBGitRevSpecifier *rev in repository.branches) [self addRevSpec:rev]; @@ -301,6 +323,7 @@ - (void)populateList [items addObject:branches]; [items addObject:remotes]; [items addObject:tags]; + [items addObject:stashes]; [items addObject:submodules]; [items addObject:others]; @@ -308,6 +331,7 @@ - (void)populateList [sourceView expandItem:project]; [sourceView expandItem:branches expandChildren:YES]; [sourceView expandItem:remotes]; + [sourceView expandItem:stashes]; [sourceView expandItem:submodules]; [sourceView reloadItem:nil reloadChildren:YES]; diff --git a/Classes/Controllers/PBGitWindowController.h b/Classes/Controllers/PBGitWindowController.h index eebf93777..581ec1677 100644 --- a/Classes/Controllers/PBGitWindowController.h +++ b/Classes/Controllers/PBGitWindowController.h @@ -48,6 +48,9 @@ - (IBAction) revealInFinder:(id)sender; - (IBAction) openInTerminal:(id)sender; - (IBAction) refresh:(id)sender; +- (IBAction) stashSave:(id) sender; +- (IBAction) stashSaveWithKeepIndex:(id) sender; +- (IBAction) stashPop:(id) sender; - (void)setHistorySearch:(NSString *)searchString mode:(NSInteger)mode; diff --git a/Classes/Controllers/PBGitWindowController.m b/Classes/Controllers/PBGitWindowController.m index 245056926..498c425c2 100644 --- a/Classes/Controllers/PBGitWindowController.m +++ b/Classes/Controllers/PBGitWindowController.m @@ -254,6 +254,27 @@ - (void)hideModalSheet:(RJModalRepoSheet *)sheet } } +#pragma mark SplitView Delegates + +- (IBAction) stashSave:(id) sender +{ + [repository stashSaveWithKeepIndex:NO]; +} + +- (IBAction) stashSaveWithKeepIndex:(id) sender +{ + [repository stashSaveWithKeepIndex:YES]; +} + +- (IBAction) stashPop:(id) sender +{ + if ([repository.stashes count] > 0) { + PBGitStash * latestStash = [repository.stashes objectAtIndex:0]; + [repository stashPop:latestStash]; + } +} + + #pragma mark - #pragma mark SplitView Delegates diff --git a/Classes/Controllers/PBRefController.m b/Classes/Controllers/PBRefController.m index 3913851ea..86ed16d29 100644 --- a/Classes/Controllers/PBRefController.m +++ b/Classes/Controllers/PBRefController.m @@ -234,6 +234,41 @@ - (void) diffWithHEAD:(PBRefMenuItem *)sender [PBDiffWindowController showDiffWindowWithFiles:nil fromCommit:commit diffCommit:nil]; } +#pragma mark Stash + +-(void) stashPop:(id)sender +{ + PBGitStash * stash = [historyController.repository stashForRef:[sender refish]]; + BOOL ok = [historyController.repository stashPop:stash]; + if (ok) { + [historyController.repository.windowController showCommitView:sender]; + } +} + +-(void) stashApply:(id)sender +{ + PBGitStash * stash = [historyController.repository stashForRef:[sender refish]]; + BOOL ok = [historyController.repository stashApply:stash]; + if (ok) { + [historyController.repository.windowController showCommitView:sender]; + } +} + +-(void) stashDrop:(id)sender +{ + PBGitStash * stash = [historyController.repository stashForRef:[sender refish]]; + BOOL ok = [historyController.repository stashDrop:stash]; + if (ok) { + [historyController.repository.windowController showHistoryView:sender]; + } +} + +-(void) stashViewDiff:(id)sender +{ + PBGitStash * stash = [historyController.repository stashForRef:[sender refish]]; + [PBDiffWindowController showDiffWindowWithFiles:nil fromCommit:stash.ancesterCommit diffCommit:stash.commit]; +} + #pragma mark Tags - (void) createTag:(PBRefMenuItem *)sender diff --git a/Classes/Views/PBFileChangesTableView.m b/Classes/Views/PBFileChangesTableView.m index ba73e3a6e..a0ce4a554 100644 --- a/Classes/Views/PBFileChangesTableView.m +++ b/Classes/Views/PBFileChangesTableView.m @@ -70,4 +70,19 @@ - (void)keyDown:(NSEvent *)theEvent } } +-(BOOL)acceptsFirstResponder +{ + return [self numberOfRows] > 0; +} + +-(NSView *)nextKeyView +{ + return [(PBGitIndexController*)[self delegate] nextKeyViewFor:self]; +} + +-(NSView *)previousKeyView +{ + return [(PBGitIndexController*)[self delegate] previousKeyViewFor:self]; +} + @end diff --git a/Classes/Views/PBRefMenuItem.m b/Classes/Views/PBRefMenuItem.m index fed45abe4..46137c303 100644 --- a/Classes/Views/PBRefMenuItem.m +++ b/Classes/Views/PBRefMenuItem.m @@ -30,11 +30,48 @@ + (PBRefMenuItem *) separatorItem } ++ (NSArray *) defaultMenuItemsForStashRef:(PBGitRef *)ref inRepository:(PBGitRepository *)repo target:(id)target +{ + NSMutableArray *items = [NSMutableArray array]; + NSString *targetRefName = [ref shortName]; + BOOL isCleanWorkingCopy = YES; + + // pop + NSString *stashPopTitle = [NSString stringWithFormat:@"Pop %@", targetRefName]; + [items addObject:[PBRefMenuItem itemWithTitle:stashPopTitle action:@selector(stashPop:) enabled:isCleanWorkingCopy]]; + + // apply + NSString *stashApplyTitle = @"Apply"; + [items addObject:[PBRefMenuItem itemWithTitle:stashApplyTitle action:@selector(stashApply:) enabled:YES]]; + + // view diff + NSString *stashDiffTitle = @"View Diff"; + [items addObject:[PBRefMenuItem itemWithTitle:stashDiffTitle action:@selector(stashViewDiff:) enabled:YES]]; + + [items addObject:[PBRefMenuItem separatorItem]]; + + // drop + NSString *stashDropTitle = @"Drop"; + [items addObject:[PBRefMenuItem itemWithTitle:stashDropTitle action:@selector(stashDrop:) enabled:YES]]; + + for (PBRefMenuItem *item in items) { + [item setTarget:target]; + [item setRefish:ref]; + } + + return items; +} + + + (NSArray *) defaultMenuItemsForRef:(PBGitRef *)ref inRepository:(PBGitRepository *)repo target:(id)target { if (!ref || !repo || !target) { return nil; } + + if ([ref isStash]) { + return [self defaultMenuItemsForStashRef:ref inRepository:repo target:target]; + } NSMutableArray *items = [NSMutableArray array]; diff --git a/Classes/git/PBGitRef.h b/Classes/git/PBGitRef.h index 80bae6e29..6a1fb311f 100644 --- a/Classes/git/PBGitRef.h +++ b/Classes/git/PBGitRef.h @@ -14,10 +14,12 @@ extern NSString * const kGitXTagType; extern NSString * const kGitXBranchType; extern NSString * const kGitXRemoteType; extern NSString * const kGitXRemoteBranchType; +extern NSString * const kGitXStashType; extern NSString * const kGitXTagRefPrefix; extern NSString * const kGitXBranchRefPrefix; extern NSString * const kGitXRemoteRefPrefix; +extern NSString * const kGitXStashRefPrefix; @interface PBGitRef : NSObject { @@ -39,6 +41,7 @@ extern NSString * const kGitXRemoteRefPrefix; - (BOOL) isTag; - (BOOL) isRemote; - (BOOL) isRemoteBranch; +- (BOOL) isStash; - (PBGitRef *) remoteRef; diff --git a/Classes/git/PBGitRef.m b/Classes/git/PBGitRef.m index c9ab938ae..90d1ad892 100644 --- a/Classes/git/PBGitRef.m +++ b/Classes/git/PBGitRef.m @@ -13,10 +13,12 @@ NSString * const kGitXBranchType = @"branch"; NSString * const kGitXRemoteType = @"remote"; NSString * const kGitXRemoteBranchType = @"remote branch"; +NSString * const kGitXStashType = @"stash"; NSString * const kGitXTagRefPrefix = @"refs/tags/"; NSString * const kGitXBranchRefPrefix = @"refs/heads/"; NSString * const kGitXRemoteRefPrefix = @"refs/remotes/"; +NSString * const kGitXStashRefPrefix = @"refs/stash@"; @implementation PBGitRef @@ -63,6 +65,8 @@ - (NSString *) type return @"tag"; if ([self isRemote]) return @"remote"; + if ([self isStash]) + return @"stash"; return nil; } @@ -89,6 +93,11 @@ - (BOOL) isRemoteBranch return ([[ref componentsSeparatedByString:@"/"] count] > 3); } +- (BOOL) isStash +{ + return [ref hasPrefix:kGitXStashRefPrefix]; +} + - (BOOL) isEqualToRef:(PBGitRef *)otherRef { return [ref isEqualToString:[otherRef ref]]; @@ -132,6 +141,8 @@ - (NSString *) refishName - (NSString *) shortName { + if ([self isStash]) + return [ref substringFromIndex:5]; if ([self type]) return [ref substringFromIndex:[[self type] length] + 7]; return ref; @@ -147,6 +158,8 @@ - (NSString *) refishType return kGitXRemoteBranchType; if ([self isRemote]) return kGitXRemoteType; + if ([self isStash]) + return kGitXStashType; return nil; } diff --git a/Classes/git/PBGitRepository.h b/Classes/git/PBGitRepository.h index 2cda588f4..ef269a37b 100644 --- a/Classes/git/PBGitRepository.h +++ b/Classes/git/PBGitRepository.h @@ -10,6 +10,7 @@ #import "PBGitHistoryList.h" #import "PBGitRevSpecifier.h" #import "PBGitRefish.h" +#import "PBGitStash.h" @class GTRepository; @class GTConfiguration; @@ -60,6 +61,7 @@ static NSString * PBStringFromBranchFilterType(PBGitXBranchFilterType type) { @property (readonly, getter = getIndexURL) NSURL* indexURL; @property (nonatomic, strong) PBGitHistoryList *revisionList; +@property (nonatomic, readonly, strong) NSArray* stashes; @property (nonatomic, readonly, strong) NSArray* branches; @property (nonatomic, strong) NSMutableOrderedSet* branchesSet; @property (nonatomic, strong) PBGitRevSpecifier* currentBranch; @@ -82,6 +84,11 @@ static NSString * PBStringFromBranchFilterType(PBGitXBranchFilterType type) { - (BOOL) createTag:(NSString *)tagName message:(NSString *)message atRefish:(id )commitSHA; - (BOOL) deleteRemote:(PBGitRef *)ref; - (BOOL) deleteRef:(PBGitRef *)ref; +- (BOOL) stashPop:(PBGitStash *)stash; +- (BOOL) stashApply:(PBGitStash *)stash; +- (BOOL) stashDrop:(PBGitStash *)stash; +- (BOOL) stashSave; +- (BOOL) stashSaveWithKeepIndex:(BOOL)keepIndex; - (NSURL *) gitURL ; @@ -116,6 +123,7 @@ static NSString * PBStringFromBranchFilterType(PBGitXBranchFilterType type) { - (PBGitSHA *)shaForRef:(PBGitRef *)ref; - (PBGitCommit *)commitForRef:(PBGitRef *)ref; - (PBGitCommit *)commitForSHA:(PBGitSHA *)sha; +- (PBGitStash *)stashForRef:(PBGitRef *)ref; - (BOOL)isOnSameBranch:(PBGitSHA *)baseSHA asSHA:(PBGitSHA *)testSHA; - (BOOL)isSHAOnHeadBranch:(PBGitSHA *)testSHA; - (BOOL)isRefOnHeadBranch:(PBGitRef *)testRef; diff --git a/Classes/git/PBGitRepository.m b/Classes/git/PBGitRepository.m index f0e1d07df..aaa5004a5 100644 --- a/Classes/git/PBGitRepository.m +++ b/Classes/git/PBGitRepository.m @@ -23,10 +23,13 @@ #import "PBGitRepositoryWatcher.h" #import "GitRepoFinder.h" #import "PBGitSubmodule.h" +#import "PBGitStash.h" #import #import #import +#import "git2.h" + NSString *PBGitRepositoryDocumentType = @"Git Repository"; @@ -291,7 +294,9 @@ - (void) reloadRefs [self loadSubmodules]; [self willChangeValueForKey:@"refs"]; + [self willChangeValueForKey:@"stashes"]; [self didChangeValueForKey:@"refs"]; + [self didChangeValueForKey:@"stashes"]; [[[self windowController] window] setTitle:[self displayName]]; } @@ -548,6 +553,115 @@ - (NSString *) workingDirectory } } +#pragma mark Stashes + +typedef void (^PBRepositoryStashEnumerationBlock)(PBGitStash *stash, BOOL *stop); + +typedef struct { + __unsafe_unretained id repository; + __unsafe_unretained PBRepositoryStashEnumerationBlock block; +} PBRepositoryStashEnumerationInfo; + + +static int stashEnumerationCallback(size_t index, + const char* message, + const git_oid *stash_id, + void *payload) { + PBRepositoryStashEnumerationInfo *info = payload; + + NSString* stashMsg = [NSString stringWithUTF8String:message]; + PBGitStash * stash = [[PBGitStash alloc] initWithRepository:info->repository stashOID:*stash_id index:index message:stashMsg]; + BOOL stop = NO; + info->block(stash, &stop); + if (stop) return 1; + + return 0; +} + + +- (void)enumerateStashesUsingBlock:(void (^)(PBGitStash *stash, BOOL *stop))block { + NSParameterAssert(block != nil); + + // Enumeration is synchronous, so it's okay for the objects here to be + // unretained for the duration. + PBRepositoryStashEnumerationInfo info = { + .repository = self, + .block = block + }; + + git_stash_foreach(self.gtRepo.git_repository, &stashEnumerationCallback, &info); +} + +- (NSArray *) stashes +{ + NSMutableArray * stashes = [NSMutableArray array]; + [self enumerateStashesUsingBlock:^(PBGitStash *stash, BOOL *stop) { + [stashes addObject:stash]; + }]; + return [NSArray arrayWithArray:stashes]; +} + +- (PBGitStash *)stashForRef:(PBGitRef *)ref { + __block PBGitStash * found = nil; + [self enumerateStashesUsingBlock:^(PBGitStash *stash, BOOL *stop) { + if ([stash.ref isEqualToRef:ref]) { + found = stash; + stop = YES; + } + }]; + return found; +} + +-(BOOL)stashRunCommand:(NSString *)command withStash:(PBGitStash *)stash +{ + int retValue; + NSArray *arguments = @[@"stash", command, stash.ref.refishName]; + NSString *output = [self outputInWorkdirForArguments:arguments retValue:&retValue]; + [self willChangeValueForKey:@"stashes"]; + [self didChangeValueForKey:@"stashes"]; + if (retValue) { + NSString *title = [NSString stringWithFormat:@"Stash %@ failed!", command]; + NSString *message = [NSString stringWithFormat:@"There was an error!"]; + [self.windowController showErrorSheetTitle:title message:message arguments:arguments output:output]; + } + return retValue ? NO : YES; +} + +-(BOOL)stashPop:(PBGitStash *)stash +{ + return [self stashRunCommand:@"pop" withStash:stash]; +} + +-(BOOL)stashApply:(PBGitStash *)stash +{ + return [self stashRunCommand:@"apply" withStash:stash]; +} + +-(BOOL)stashDrop:(PBGitStash *)stash +{ + return [self stashRunCommand:@"drop" withStash:stash]; +} + +-(BOOL)stashSave +{ + return [self stashSaveWithKeepIndex:NO]; +} + +-(BOOL)stashSaveWithKeepIndex:(BOOL)keepIndex +{ + int retValue; + NSArray * arguments = @[@"stash", @"save", keepIndex?@"--keep-index":@"--no-keep-index"]; + NSString * output = [self outputInWorkdirForArguments:arguments retValue:&retValue]; + [self willChangeValueForKey:@"stashes"]; + [self didChangeValueForKey:@"stashes"]; + if (retValue) { + NSString *title = [NSString stringWithFormat:@"Stash save failed!"]; + NSString *message = [NSString stringWithFormat:@"There was an error!"]; + [self.windowController showErrorSheetTitle:title message:message arguments:arguments output:output]; + } + return retValue ? NO : YES; +} + #pragma mark Remotes - (NSArray *) remotes diff --git a/Classes/git/PBGitSVStashItem.h b/Classes/git/PBGitSVStashItem.h new file mode 100644 index 000000000..332a37a92 --- /dev/null +++ b/Classes/git/PBGitSVStashItem.h @@ -0,0 +1,19 @@ +// +// PBSourceViewStash.h +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import +#import "PBSourceViewItem.h" +#import "PBGitStash.h" + +@interface PBGitSVStashItem : PBSourceViewItem + ++ (id) itemWithStash:(PBGitStash*)stash; + +@property (nonatomic, strong) PBGitStash* stash; + +@end diff --git a/Classes/git/PBGitSVStashItem.m b/Classes/git/PBGitSVStashItem.m new file mode 100644 index 000000000..ccdd67829 --- /dev/null +++ b/Classes/git/PBGitSVStashItem.m @@ -0,0 +1,28 @@ +// +// PBSourceViewStash.m +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import "PBGitSVStashItem.h" +#import "PBGitRevSpecifier.h" + + +@implementation PBGitSVStashItem + ++ (id)itemWithStash:(PBGitStash *)stash +{ + NSString * title = [NSString stringWithFormat:@"@{%zd}: %@", stash.index, stash.message]; + PBGitSVStashItem * item = [self itemWithTitle:title]; + item.stash = stash; + item.revSpecifier = [[PBGitRevSpecifier alloc] initWithRef:stash.ref]; + return item; +} + +-(PBGitRef *)ref { + return self.stash.ref; +} + +@end diff --git a/Classes/git/PBGitStash.h b/Classes/git/PBGitStash.h new file mode 100644 index 000000000..55b112da8 --- /dev/null +++ b/Classes/git/PBGitStash.h @@ -0,0 +1,27 @@ +// +// PBGitStash.h +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import +#import + +@class PBGitCommit; +@class PBGitRef; +@class PBGitRepository; + +@interface PBGitStash : NSObject +@property (nonatomic, readonly) size_t index; +@property (nonatomic, readonly) PBGitCommit * commit; +@property (nonatomic, readonly) NSString* message; +@property (nonatomic, readonly) PBGitRef* ref; + +@property (nonatomic, readonly) PBGitCommit * indexCommit; +@property (nonatomic, readonly) PBGitCommit * ancesterCommit; + +- (id) initWithRepository:(PBGitRepository *)repo stashOID:(git_oid)stash_id index:(size_t)index message:(NSString *)message; + +@end diff --git a/Classes/git/PBGitStash.m b/Classes/git/PBGitStash.m new file mode 100644 index 000000000..d05a2c6f9 --- /dev/null +++ b/Classes/git/PBGitStash.m @@ -0,0 +1,57 @@ +// +// PBGitStash.m +// GitX +// +// Created by Mathias Leppich on 8/1/13. +// +// + +#import "PBGitStash.h" +#import "PBGitRef.h" +#import "PBGitCommit.h" +#import + +@implementation PBGitStash + +@synthesize index = _index; +@synthesize commit = _commit; +@synthesize message = _message; +@synthesize indexCommit = _indexCommit; +@synthesize ancesterCommit = _ancesterCommit; + +-(id)initWithRepository:(PBGitRepository *)repo stashOID:(git_oid)stash_id index:(size_t)index message:(NSString *)message +{ + _index = index; + _message = message; + + GTRepository * gtRepo = repo.gtRepo; + NSError * error = nil; + GTCommit * gtCommit = (GTCommit *)[gtRepo lookupObjectByOid:&stash_id objectType:GTObjectTypeCommit error:&error]; + NSArray * parents = [gtCommit parents]; + GTCommit * gtIndexCommit = [parents objectAtIndex:1]; + GTCommit * gtAncestorCommit = [parents objectAtIndex:0]; + + PBGitSHA * sha = [PBGitSHA shaWithOID:stash_id]; + PBGitSHA * indexSha = [PBGitSHA shaWithOID:*git_object_id(gtIndexCommit.git_object)]; + PBGitSHA * ancestorSha = [PBGitSHA shaWithOID:*git_object_id(gtAncestorCommit.git_object)]; + + _commit = [PBGitCommit commitWithRepository:repo andSha:sha]; + _indexCommit = [PBGitCommit commitWithRepository:repo andSha:indexSha]; + _ancesterCommit = [PBGitCommit commitWithRepository:repo andSha:ancestorSha]; + + //NSLog(@" stash: %zd, %@, %@, %@",_index,[_commit shortName], [_ancesterCommit shortName], [_indexCommit shortName]); + return self; +} + +-(NSString *)description +{ + return [NSString stringWithFormat:@"stash@{%zd}: %@", _index, _message]; +} + +-(PBGitRef *)ref +{ + NSString * refStr = [NSString stringWithFormat:@"refs/stash@{%zd}", _index]; + return [[PBGitRef alloc] initWithString:refStr]; +} + +@end diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index a94dde645..0685dca44 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -700,6 +700,44 @@ + + + Stash Save + y + 1048576 + 2147483647 + + + + + + YES + Stash Save - Keep Index + y + 1572864 + 2147483647 + + + + + + Stash Pop + Y + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + Open in Terminal @@ -1320,6 +1358,30 @@ 968 + + + stashSave: + + + + 990 + + + + stashSaveWithKeepIndex: + + + + 991 + + + + stashPop: + + + + 992 + showAboutPanel: @@ -2017,10 +2079,14 @@ - + + + + + @@ -2034,11 +2100,6 @@ - - 943 - - - 947 @@ -2085,6 +2146,31 @@ Menu Item - Change Log + + 977 + + + + + 986 + + + + + 987 + + + + + 988 + + + + + 989 + + + @@ -2185,7 +2271,6 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2195,12 +2280,17 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin - 976 + 992 @@ -2211,7 +2301,6 @@ id id id - id id id id @@ -2230,10 +2319,6 @@ reportAProblem: id - - saveAction: - id - showAboutPanel: id @@ -2363,47 +2448,6 @@ ./Classes/MGScopeBar.h - - NSDocument - - id - id - id - id - id - id - - - - printDocument: - id - - - revertDocumentToSaved: - id - - - runPageLayout: - id - - - saveDocument: - id - - - saveDocumentAs: - id - - - saveDocumentTo: - id - - - - IBProjectSource - ./Classes/NSDocument.h - - PBCollapsibleSplitView PBNiceSplitView @@ -2529,6 +2573,7 @@ id id id + id @@ -2547,13 +2592,20 @@ signOff: id + + stashChanges: + id + NSArrayController NSButton NSTextView PBNiceSplitView + NSTabView PBGitIndexController + NSButton + NSArrayController NSArrayController PBWebChangesController @@ -2574,10 +2626,22 @@ commitSplitView PBNiceSplitView + + controlsTabView + NSTabView + indexController PBGitIndexController + + stashButton + NSButton + + + trackedFilesController + NSArrayController + unstagedFilesController NSArrayController @@ -2841,6 +2905,9 @@ id id id + id + id + id @@ -2863,6 +2930,18 @@ showHistoryView: id + + stashPop: + id + + + stashSave: + id + + + stashSaveWithKeepIndex: + id + NSView @@ -3150,24 +3229,6 @@ ./Classes/SUUpdater.h - - WebView - - reloadFromOrigin: - id - - - reloadFromOrigin: - - reloadFromOrigin: - id - - - - IBProjectSource - ./Classes/WebView.h - - 0 diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index c6fcd3205..a185f669e 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -181,6 +181,8 @@ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 911112370E5A097800BF76B4 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 911112360E5A097800BF76B4 /* Security.framework */; }; 913D5E500E55645900CECEA2 /* gitx in Resources */ = {isa = PBXBuildFile; fileRef = 913D5E490E55644600CECEA2 /* gitx */; }; + A2F8D0DF17AAB32500580B84 /* PBGitStash.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F8D0DE17AAB32500580B84 /* PBGitStash.m */; }; + A2F8D0EB17AAB95E00580B84 /* PBGitSVStashItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F8D0EA17AAB95E00580B84 /* PBGitSVStashItem.m */; }; BC0444AD17648CC900353E6D /* AddRemoteTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = BC0444AC17648CC900353E6D /* AddRemoteTemplate.png */; }; BC0444B817648D0200353E6D /* BranchHighlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = BC0444B717648D0200353E6D /* BranchHighlighted.png */; }; BC0444BA17648DA800353E6D /* FolderHighlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = BC0444B917648DA700353E6D /* FolderHighlighted.png */; }; @@ -644,6 +646,10 @@ 8D1107320486CEB800E47090 /* GitX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GitX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 911112360E5A097800BF76B4 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; 913D5E490E55644600CECEA2 /* gitx */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gitx; sourceTree = BUILT_PRODUCTS_DIR; }; + A2F8D0DD17AAB32500580B84 /* PBGitStash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitStash.h; sourceTree = ""; }; + A2F8D0DE17AAB32500580B84 /* PBGitStash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitStash.m; sourceTree = ""; }; + A2F8D0E917AAB95E00580B84 /* PBGitSVStashItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSVStashItem.h; sourceTree = ""; }; + A2F8D0EA17AAB95E00580B84 /* PBGitSVStashItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSVStashItem.m; sourceTree = ""; }; BC0444AC17648CC900353E6D /* AddRemoteTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AddRemoteTemplate.png; sourceTree = ""; }; BC0444B717648D0200353E6D /* BranchHighlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BranchHighlighted.png; sourceTree = ""; }; BC0444B917648DA700353E6D /* FolderHighlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = FolderHighlighted.png; sourceTree = ""; }; @@ -1085,6 +1091,8 @@ 4A5D767C14A9A9CC00DF6C68 /* PBGitSVStageItem.m */, 4A5D767D14A9A9CC00DF6C68 /* PBGitSVTagItem.h */, 4A5D767E14A9A9CC00DF6C68 /* PBGitSVTagItem.m */, + A2F8D0E917AAB95E00580B84 /* PBGitSVStashItem.h */, + A2F8D0EA17AAB95E00580B84 /* PBGitSVStashItem.m */, 4A5D767F14A9A9CC00DF6C68 /* PBGitTree.h */, 4A5D768014A9A9CC00DF6C68 /* PBGitTree.m */, 4A5D768114A9A9CC00DF6C68 /* PBGitXErrors.h */, @@ -1093,6 +1101,8 @@ 4A5D768414A9A9CC00DF6C68 /* PBGitXProtocol.m */, 643952721603E9BC00BB7AFF /* PBGitSubmodule.h */, 643952731603E9BC00BB7AFF /* PBGitSubmodule.m */, + A2F8D0DD17AAB32500580B84 /* PBGitStash.h */, + A2F8D0DE17AAB32500580B84 /* PBGitStash.m */, 643952751603EF9B00BB7AFF /* PBGitSVSubmoduleItem.h */, 643952761603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m */, 4AB057E11652652000DE751D /* GitRepoFinder.h */, @@ -1642,6 +1652,8 @@ 643952741603E9BC00BB7AFF /* PBGitSubmodule.m in Sources */, 643952771603EF9B00BB7AFF /* PBGitSVSubmoduleItem.m in Sources */, 4AB057E31652652000DE751D /* GitRepoFinder.m in Sources */, + A2F8D0DF17AAB32500580B84 /* PBGitStash.m in Sources */, + A2F8D0EB17AAB95E00580B84 /* PBGitSVStashItem.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Resources/XIBs/PBGitCommitView.xib b/Resources/XIBs/PBGitCommitView.xib index d5626e548..08046226c 100644 --- a/Resources/XIBs/PBGitCommitView.xib +++ b/Resources/XIBs/PBGitCommitView.xib @@ -2,13 +2,13 @@ 1060 - 12A269 - 2549 - 1187 - 624.00 + 12E55 + 3084 + 1187.39 + 626.00 - 2549 - 1460 + 3084 + 2053 NSArrayController @@ -20,11 +20,14 @@ NSScrollView NSScroller NSSplitView + NSTabView + NSTabViewItem NSTableColumn NSTableView NSTextFieldCell NSTextView NSUserDefaultsController + NSView WebView @@ -298,33 +301,6 @@ 274 - - - 289 - {{339, 0}, {96, 32}} - - - - YES - - 67108864 - 134217728 - Commit - - LucidaGrande - 13 - 1044 - - - -2038284288 - 268435585 - - DQ - 200 - 25 - - NO - 274 @@ -378,7 +354,7 @@ 1 - 298883 + 67407747 0 @@ -446,7 +422,7 @@ {{346, 1}, {15, 164}} - + NO _doScroller: @@ -479,58 +455,193 @@ 4 1 - + - 292 - {{-2, 9}, {82, 18}} + 34 + {{0, 4}, {429, 30}} - - YES - - -2080374784 - 0 - Amend - - - 1211912448 - 402653186 - - NSImage - NSSwitch + + _NS:9 + + + 1 + + + 256 + + + + 292 + {{-2, 6}, {82, 18}} + + + YES + + -2080374784 + 0 + Amend + + LucidaGrande + 13 + 1044 + + + 1211912448 + 402653186 + + NSImage + NSSwitch + + + NSSwitch + + + a + 200 + 25 + + NO + + + + 289 + {{243, -2}, {96, 32}} + + + YES + + 67108864 + 134217728 + Sign-Off + + + -2038284288 + 129 + + + 200 + 25 + + NO + + + + 289 + {{339, -2}, {96, 32}} + + + YES + + 67108864 + 134217728 + Commit + + + -2038284288 + 268435585 + + DQ + 200 + 25 + + NO + + + {429, 30} + + _NS:11 + + Commit + + 6 + System + controlColor + + + - - NSSwitch + + 2 + + + 256 + + + + 265 + {{302, -2}, {133, 32}} + + + + _NS:9 + YES + + 67108864 + 134217728 + Stash Changes + + _NS:9 + + -2038284288 + 129 + + + 200 + 25 + + NO + + + + 268 + {{-2, 6}, {93, 18}} + + + + _NS:9 + YES + + -2080374784 + 268435456 + Keep Index + + _NS:9 + + 1211912448 + 2 + + + + + 200 + 25 + + NO + + + {429, 30} + + + + _NS:28 + + Stash + + - - a - 200 - 25 - - NO - - - - 289 - {{243, 0}, {96, 32}} - - - - YES - - 67108864 - 134217728 - Sign-Off - - - -2038284288 - 129 - - - 200 - 25 + + + + LucidaGrande + 9 + 3614 - NO + 268435462 + YES + + + {429, 227} @@ -761,6 +872,15 @@ YES YES + + YES + + YES + YES + YES + YES + YES + PBWebChangesController @@ -794,14 +914,6 @@ 156 - - - commit: - - - - 212 - commitMessageView @@ -826,6 +938,30 @@ 256 + + + commitSplitView + + + + 314 + + + + commitButton + + + + 307 + + + + commit: + + + + 212 + signOff: @@ -836,19 +972,35 @@ - commitButton + controlsTabView - + - 307 + 324 + + + + stashChanges: + + + + 325 - commitSplitView + trackedFilesController - + - 314 + 341 + + + + stashButton + + + + 342 @@ -1074,6 +1226,38 @@ 269 + + + value: self.stashKeepIndex + + + + + + value: self.stashKeepIndex + value + self.stashKeepIndex + 2 + + + 333 + + + + contentArray: index.indexChanges + + + + + + contentArray: index.indexChanges + contentArray + index.indexChanges + 2 + + + 340 + @@ -1169,9 +1353,7 @@ - - - + @@ -1224,14 +1406,6 @@ - - 163 - - - - - - 130 @@ -1257,11 +1431,6 @@ - - 164 - - - 54 @@ -1303,13 +1472,62 @@ + + 254 + + + + + 315 + + + + + + + + + 316 + + + + + + + + 317 + + + + + + + + 318 + + + + + + + + + 319 + + + + + + + + 247 - + 248 @@ -1317,9 +1535,17 @@ - 254 - - + 163 + + + + + + + + 164 + + 278 @@ -1327,13 +1553,45 @@ - + 279 + + 320 + + + + + + + + 321 + + + + + 322 + + + + + + + + 323 + + + + + 335 + + + Tracked Files + @@ -1366,6 +1624,24 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + + InitialTabViewItem + + InitialTabViewItem + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1385,7 +1661,7 @@ - 314 + 342 @@ -1413,6 +1689,7 @@ id id id + id @@ -1431,13 +1708,20 @@ signOff: id + + stashChanges: + id + NSArrayController NSButton NSTextView PBNiceSplitView + NSTabView PBGitIndexController + NSButton + NSArrayController NSArrayController PBWebChangesController @@ -1458,10 +1742,22 @@ commitSplitView PBNiceSplitView + + controlsTabView + NSTabView + indexController PBGitIndexController + + stashButton + NSButton + + + trackedFilesController + NSArrayController + unstagedFilesController NSArrayController @@ -1616,24 +1912,6 @@ ./Classes/PBWebController.h - - WebView - - reloadFromOrigin: - id - - - reloadFromOrigin: - - reloadFromOrigin: - id - - - - IBProjectSource - ./Classes/WebView.h - - 0