WIP: Add GUI support for the new index

This commit is contained in:
Pieter de Bie
2009-09-13 01:59:48 +02:00
parent 6531cfce3b
commit 03e1d3e882
5 changed files with 88 additions and 330 deletions
+5 -21
View File
@@ -9,12 +9,10 @@
#import <Cocoa/Cocoa.h>
#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;
+23 -250
View File
@@ -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
+54 -55
View File
@@ -2,15 +2,14 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">9J61</string>
<string key="IBDocument.SystemVersion">9L31a</string>
<string key="IBDocument.InterfaceBuilderVersion">677</string>
<string key="IBDocument.AppKitVersion">949.46</string>
<string key="IBDocument.AppKitVersion">949.54</string>
<string key="IBDocument.HIToolboxVersion">353.00</string>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="207"/>
<integer value="225"/>
<integer value="113"/>
<integer value="1" id="9"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -127,7 +126,7 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="12" id="777559147"/>
<reference ref="777559147"/>
<reference ref="9"/>
<integer value="1" id="9"/>
</object>
</object>
</object>
@@ -963,38 +962,6 @@
</object>
<int key="connectionID">139</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">contentArray: files</string>
<reference key="source" ref="667905213"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="667905213"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">contentArray: files</string>
<string key="NSBinding">contentArray</string>
<string key="NSKeyPath">files</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">149</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">contentArray: files</string>
<reference key="source" ref="128809524"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="128809524"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">contentArray: files</string>
<string key="NSBinding">contentArray</string>
<string key="NSKeyPath">files</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">150</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">cachedFilesController</string>
@@ -1067,22 +1034,6 @@
</object>
<int key="connectionID">241</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: amend</string>
<reference key="source" ref="18874447"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="18874447"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">value: amend</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">amend</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">252</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">webController</string>
@@ -1219,6 +1170,54 @@
</object>
<int key="connectionID">280</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">contentArray: index.indexChanges</string>
<reference key="source" ref="128809524"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="128809524"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">contentArray: index.indexChanges</string>
<string key="NSBinding">contentArray</string>
<string key="NSKeyPath">index.indexChanges</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">281</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">contentArray: index.indexChanges</string>
<reference key="source" ref="667905213"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="667905213"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">contentArray: index.indexChanges</string>
<string key="NSBinding">contentArray</string>
<string key="NSKeyPath">index.indexChanges</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">282</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: index.amend</string>
<reference key="source" ref="18874447"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="18874447"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">value: index.amend</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">index.amend</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">283</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -1616,7 +1615,7 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilderKit</string>
<string>com.apple.InterfaceBuilderKit</string>
<string>{{59, 63}, {852, 432}}</string>
<string>{{428, 510}, {852, 432}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<integer value="0" id="8"/>
<reference ref="8"/>
@@ -1680,7 +1679,7 @@
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">280</int>
<int key="maxID">283</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+1 -1
View File
@@ -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
+5 -3
View File
@@ -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];
}
}