diff --git a/PBGitCommitController.h b/PBGitCommitController.h index 0976738..f96e986 100644 --- a/PBGitCommitController.h +++ b/PBGitCommitController.h @@ -9,12 +9,10 @@ #import #import "PBViewController.h" -@class PBGitIndexController; -@class PBIconAndTextCell; -@class PBWebChangesController; +@class PBGitIndexController, PBIconAndTextCell, PBWebChangesController, PBGitIndex; @interface PBGitCommitController : PBViewController { - NSMutableArray *files; + PBGitIndex *index; IBOutlet NSTextView *commitMessageView; IBOutlet NSArrayController *unstagedFilesController; @@ -25,28 +23,14 @@ NSString *status; - // We use busy as a count of active processes. - // You can increase it when your process start - // And decrease it after you have finished. - int busy; - BOOL amend; NSDictionary *amendEnvironment; } -@property (retain) NSMutableArray *files; -@property (copy) NSString *status; -@property (assign) int busy; -@property (assign) BOOL amend; - -- (void) readCachedFiles:(NSNotification *)notification; -- (void) readOtherFiles:(NSNotification *)notification; -- (void) readUnstagedFiles:(NSNotification *)notification; -- (void) stageHunk: (NSString *)hunk reverse:(BOOL)reverse; -- (void)discardHunk:(NSString *)hunk; - -- (NSString *)parentTree; +@property(copy) NSString *status; +@property(readonly) PBGitIndex *index; +// FIXME: redo 0823ff859dc65db238951c16e49293ea411c2b5a - (IBAction) refresh:(id) sender; - (IBAction) commit:(id) sender; - (IBAction)signOff:(id)sender; diff --git a/PBGitCommitController.m b/PBGitCommitController.m index 1e894ee..be3f309 100644 --- a/PBGitCommitController.m +++ b/PBGitCommitController.m @@ -10,26 +10,36 @@ #import "NSFileHandleExt.h" #import "PBChangedFile.h" #import "PBWebChangesController.h" +#import "PBGitIndex.h" #import "NSString_RegEx.h" @interface PBGitCommitController (PrivateMethods) -- (NSArray *) linesFromNotification:(NSNotification *)notification; -- (void) doneProcessingIndex; -- (NSMutableDictionary *)dictionaryForLines:(NSArray *)lines; -- (void) addFilesFromDictionary:(NSMutableDictionary *)dictionary staged:(BOOL)staged tracked:(BOOL)tracked; - (void)processHunk:(NSString *)hunk stage:(BOOL)stage reverse:(BOOL)reverse; @end @implementation PBGitCommitController -@synthesize files, status, busy, amend; +@synthesize status, index; + +- (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller +{ + if (!(self = [super initWithRepository:theRepository superController:controller])) + return nil; + + index = [[PBGitIndex alloc] initWithRepository:theRepository workingDirectory:[NSURL fileURLWithPath:[theRepository workingDirectory]]]; + [index refresh]; + return self; +} + +- (BOOL)busy +{ + return NO; +} - (void)awakeFromNib { - self.files = [NSMutableArray array]; [super awakeFromNib]; - [self refresh:self]; [commitMessageView setTypingAttributes:[NSDictionary dictionaryWithObject:[NSFont fontWithName:@"Monaco" size:12.0] forKey:NSFontAttributeName]]; @@ -68,106 +78,9 @@ } } -- (void) setAmend:(BOOL)newAmend -{ - if (newAmend == amend) - return; - - amend = newAmend; - amendEnvironment = nil; - - // If we amend, we want to keep the author information for the previous commit - // We do this by reading in the previous commit, and storing the information - // in a dictionary. This dictionary will then later be read by [self commit:] - if (amend) { - NSString *message = [repository outputForCommand:@"cat-file commit HEAD"]; - NSArray *match = [message substringsMatchingRegularExpression:@"\nauthor ([^\n]*) <([^\n>]*)> ([0-9]+[^\n]*)\n" count:3 options:0 ranges:nil error:nil]; - if (match) - amendEnvironment = [NSDictionary dictionaryWithObjectsAndKeys:[match objectAtIndex:1], @"GIT_AUTHOR_NAME", - [match objectAtIndex:2], @"GIT_AUTHOR_EMAIL", - [match objectAtIndex:3], @"GIT_AUTHOR_DATE", - nil]; - - // Replace commit message with the old one if it's less than 3 characters long. - // This is just a random number. - if ([[commitMessageView string] length] <= 3) { - // Find the commit message - NSRange r = [message rangeOfString:@"\n\n"]; - if (r.location != NSNotFound) - message = [message substringFromIndex:r.location + 2]; - - commitMessageView.string = message; - } - } - - [self refresh:self]; -} - -- (NSArray *) linesFromNotification:(NSNotification *)notification -{ - NSDictionary *userInfo = [notification userInfo]; - NSData *data = [userInfo valueForKey:NSFileHandleNotificationDataItem]; - if (!data) - return NULL; - - NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - if (!string) - return NULL; - - // Strip trailing newline - if ([string hasSuffix:@"\n"]) - string = [string substringToIndex:[string length]-1]; - - NSArray *lines = [string componentsSeparatedByString:@"\0"]; - return lines; -} - -- (NSString *) parentTree -{ - NSString *parent = amend ? @"HEAD^" : @"HEAD"; - - if (![repository parseReference:parent]) - // We don't have a head ref. Return the empty tree. - return @"4b825dc642cb6eb9a060e54bf8d69288fbee4904"; - - return parent; -} - - (void) refresh:(id) sender { - if (![repository workingDirectory]) - return; - - self.status = @"Refreshing index…"; - - // If self.busy reaches 0, all tasks have finished - self.busy = 0; - - // Refresh the index, necessary for the next methods (that's why it's blocking) - // FIXME: Make this non-blocking. This call can be expensive in large repositories - [repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"update-index", @"-q", @"--unmerged", @"--ignore-missing", @"--refresh", nil]]; - - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:self]; - - // Other files (not tracked, not ignored) - NSArray *arguments = [NSArray arrayWithObjects:@"ls-files", @"--others", @"--exclude-standard", @"-z", nil]; - NSFileHandle *handle = [repository handleInWorkDirForArguments:arguments]; - [nc addObserver:self selector:@selector(readOtherFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - self.busy++; - [handle readToEndOfFileInBackgroundAndNotify]; - - // Unstaged files - handle = [repository handleInWorkDirForArguments:[NSArray arrayWithObjects:@"diff-files", @"-z", nil]]; - [nc addObserver:self selector:@selector(readUnstagedFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - self.busy++; - [handle readToEndOfFileInBackgroundAndNotify]; - - // Staged files - handle = [repository handleInWorkDirForArguments:[NSArray arrayWithObjects:@"diff-index", @"--cached", @"-z", [self parentTree], nil]]; - [nc addObserver:self selector:@selector(readCachedFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle]; - self.busy++; - [handle readToEndOfFileInBackgroundAndNotify]; + [index refresh]; // Reload refs (in case HEAD changed) [repository reloadRefs]; @@ -178,147 +91,9 @@ [self refresh:nil]; } -// This method is called for each of the three processes from above. -// If all three are finished (self.busy == 0), then we can delete -// all files previously marked as deletable -- (void) doneProcessingIndex -{ - // if we're still busy, do nothing :) - if (--self.busy) - return; - - NSMutableArray *deleteFiles = [NSMutableArray array]; - for (PBChangedFile *file in files) { - if (!file.hasStagedChanges && !file.hasUnstagedChanges) - [deleteFiles addObject:file]; - } - - if ([deleteFiles count]) { - [self willChangeValueForKey:@"files"]; - for (PBChangedFile *file in deleteFiles) - [files removeObject:file]; - [self didChangeValueForKey:@"files"]; - } - self.status = @"Ready"; -} - -- (void) readOtherFiles:(NSNotification *)notification; -{ - NSArray *lines = [self linesFromNotification:notification]; - NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:[lines count]]; - // We fake this files status as good as possible. - NSArray *fileStatus = [NSArray arrayWithObjects:@":000000", @"100644", @"0000000000000000000000000000000000000000", @"0000000000000000000000000000000000000000", @"A", nil]; - for (NSString *path in lines) { - if ([path length] == 0) - continue; - [dictionary setObject:fileStatus forKey:path]; - } - [self addFilesFromDictionary:dictionary staged:NO tracked:NO]; - [self doneProcessingIndex]; -} - -- (NSMutableDictionary *)dictionaryForLines:(NSArray *)lines -{ - NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[lines count]/2]; - - // Fill the dictionary with the new information - NSArray *fileStatus; - BOOL even = FALSE; - for (NSString *line in lines) { - if (!even) { - even = TRUE; - fileStatus = [line componentsSeparatedByString:@" "]; - continue; - } - - even = FALSE; - [dictionary setObject:fileStatus forKey:line]; - } - return dictionary; -} - -- (void) addFilesFromDictionary:(NSMutableDictionary *)dictionary staged:(BOOL)staged tracked:(BOOL)tracked -{ - // Iterate over all existing files - for (PBChangedFile *file in files) { - NSArray *fileStatus = [dictionary objectForKey:file.path]; - // Object found, this is still a cached / uncached thing - if (fileStatus) { - if (tracked) { - NSString *mode = [[fileStatus objectAtIndex:0] substringFromIndex:1]; - NSString *sha = [fileStatus objectAtIndex:2]; - file.commitBlobSHA = sha; - file.commitBlobMode = mode; - - if (staged) - file.hasStagedChanges = YES; - else - file.hasUnstagedChanges = YES; - } else { - // Untracked file, set status to NEW, only unstaged changes - file.hasStagedChanges = NO; - file.hasUnstagedChanges = YES; - file.status = NEW; - } - [dictionary removeObjectForKey:file.path]; - } else { // Object not found, let's remove it from the changes - if (staged) - file.hasStagedChanges = NO; - else if (tracked && file.status != NEW) // Only remove it if it's not an untracked file. We handle that with the other thing - file.hasUnstagedChanges = NO; - else if (!tracked && file.status == NEW) - file.hasUnstagedChanges = NO; - } - } - - // Do new files - if (![[dictionary allKeys] count]) - return; - - [self willChangeValueForKey:@"files"]; - for (NSString *path in [dictionary allKeys]) { - NSArray *fileStatus = [dictionary objectForKey:path]; - - PBChangedFile *file = [[PBChangedFile alloc] initWithPath:path]; - if ([[fileStatus objectAtIndex:4] isEqualToString:@"D"]) - file.status = DELETED; - else if([[fileStatus objectAtIndex:0] isEqualToString:@":000000"]) - file.status = NEW; - else - file.status = MODIFIED; - - if (tracked) { - file.commitBlobMode = [[fileStatus objectAtIndex:0] substringFromIndex:1]; - file.commitBlobSHA = [fileStatus objectAtIndex:2]; - } - - file.hasStagedChanges = staged; - file.hasUnstagedChanges = !staged; - - [files addObject: file]; - } - [self didChangeValueForKey:@"files"]; -} - -- (void) readUnstagedFiles:(NSNotification *)notification -{ - NSArray *lines = [self linesFromNotification:notification]; - NSMutableDictionary *dic = [self dictionaryForLines:lines]; - [self addFilesFromDictionary:dic staged:NO tracked:YES]; - [self doneProcessingIndex]; -} - -- (void) readCachedFiles:(NSNotification *)notification -{ - NSArray *lines = [self linesFromNotification:notification]; - NSMutableDictionary *dic = [self dictionaryForLines:lines]; - [self addFilesFromDictionary:dic staged:YES tracked:YES]; - [self doneProcessingIndex]; -} - - (void) commitFailedBecause:(NSString *)reason { - self.busy--; + //self.busy--; self.status = [@"Commit failed: " stringByAppendingString:reason]; [[repository windowController] showMessageSheet:@"Commit failed" infoText:reason]; return; @@ -360,7 +135,7 @@ [commitMessage writeToFile:commitMessageFile atomically:YES encoding:NSUTF8StringEncoding error:nil]; - self.busy++; + //self.busy++; self.status = @"Creating tree.."; NSString *tree = [repository outputForCommand:@"write-tree"]; if ([tree length] != 40) @@ -369,7 +144,7 @@ int ret; NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"commit-tree", tree, nil]; - NSString *parent = amend ? @"HEAD^" : @"HEAD"; + NSString *parent = index.amend ? @"HEAD^" : @"HEAD"; if ([repository parseReference:parent]) { [arguments addObject:@"-p"]; [arguments addObject:parent]; @@ -400,12 +175,10 @@ [webController setStateMessage:[NSString stringWithFormat:@"Successfully created commit %@", commit]]; repository.hasChanged = YES; - self.busy--; + //self.busy--; [commitMessageView setString:@""]; - amend = NO; amendEnvironment = nil; - [self refresh:self]; - self.amend = NO; + index.amend = NO; } - (void) stageHunk:(NSString *)hunk reverse:(BOOL)reverse diff --git a/PBGitCommitView.xib b/PBGitCommitView.xib index 643bfa3..49beeb5 100644 --- a/PBGitCommitView.xib +++ b/PBGitCommitView.xib @@ -2,15 +2,14 @@ 1050 - 9J61 + 9L31a 677 - 949.46 + 949.54 353.00 YES + - - YES @@ -127,7 +126,7 @@ YES - + @@ -963,38 +962,6 @@ 139 - - - contentArray: files - - - - - - contentArray: files - contentArray - files - 2 - - - 149 - - - - contentArray: files - - - - - - contentArray: files - contentArray - files - 2 - - - 150 - cachedFilesController @@ -1067,22 +1034,6 @@ 241 - - - value: amend - - - - - - value: amend - value - amend - 2 - - - 252 - webController @@ -1219,6 +1170,54 @@ 280 + + + contentArray: index.indexChanges + + + + + + contentArray: index.indexChanges + contentArray + index.indexChanges + 2 + + + 281 + + + + contentArray: index.indexChanges + + + + + + contentArray: index.indexChanges + contentArray + index.indexChanges + 2 + + + 282 + + + + value: index.amend + + + + + + value: index.amend + value + index.amend + 2 + + + 283 + @@ -1616,7 +1615,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilderKit com.apple.InterfaceBuilderKit - {{59, 63}, {852, 432}} + {{428, 510}, {852, 432}} com.apple.InterfaceBuilder.CocoaPlugin @@ -1680,7 +1679,7 @@ - 280 + 283 diff --git a/PBGitIndexController.m b/PBGitIndexController.m index 21c7076..a03b3a2 100644 --- a/PBGitIndexController.m +++ b/PBGitIndexController.m @@ -136,7 +136,7 @@ if (file.status == NEW) return [commitController.repository outputForArguments:[NSArray arrayWithObjects:@"show", indexPath, nil]]; - return [commitController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-index", [self contextParameter], @"--cached", [commitController parentTree], @"--", file.path, nil]]; + return [commitController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"diff-index", [self contextParameter], @"--cached", [commitController.index parentTree], @"--", file.path, nil]]; } - (NSString *)unstagedChangesForFile:(PBChangedFile *)file diff --git a/PBWebChangesController.m b/PBWebChangesController.m index d24a8af..697307d 100644 --- a/PBWebChangesController.m +++ b/PBWebChangesController.m @@ -8,6 +8,7 @@ #import "PBWebChangesController.h" #import "PBGitIndexController.h" +#import "PBGitIndex.h" @implementation PBWebChangesController @@ -31,7 +32,7 @@ - (BOOL) amend { - return controller.amend; + return controller.index.amend; } - (void)observeValueForKeyPath:(NSString *)keyPath @@ -83,7 +84,8 @@ - (void) stageHunk:(NSString *)hunk reverse:(BOOL)reverse { - [controller stageHunk: hunk reverse:reverse]; + //[controller stageHunk: hunk reverse:reverse]; + // FIXME: Don't need a hard refresh [self refresh]; } @@ -99,7 +101,7 @@ } if (ret == NSAlertDefaultReturn) { - [controller discardHunk:hunk]; + // [controller discardHunk:hunk]; [self refresh]; } }