From 52d9402513f5d140849b4ef8ea7459c01ba58fae Mon Sep 17 00:00:00 2001 From: Pieter de Bie Date: Wed, 18 Jun 2008 01:02:29 +0200 Subject: [PATCH 1/5] First take on graphing This implements some sort of graph shower like Gitk has. However, it still has bugs and can't do color very well. --- ApplicationController.m | 7 +- GitX.xcodeproj/project.pbxproj | 10 ++- PBGitGrapher.h | 42 +++++++++++ PBGitGrapher.m | 87 ++++++++++++++++++++++ PBGitRepository.m | 2 +- PBGitRevList.h | 3 + PBGitRevList.m | 11 ++- PBGitRevisionCell.h | 6 +- PBGitRevisionCell.m | 127 +++++++++++++++++++++++---------- 9 files changed, 247 insertions(+), 48 deletions(-) create mode 100644 PBGitGrapher.h create mode 100644 PBGitGrapher.m diff --git a/ApplicationController.m b/ApplicationController.m index 5b559df..94ad4af 100644 --- a/ApplicationController.m +++ b/ApplicationController.m @@ -225,8 +225,9 @@ if (![[aTableColumn identifier] isEqualToString:@"subject"]) return; - NSNumber* n = [NSNumber numberWithInt:(rowIndex % 2)]; - [aCell setCommit:n]; - + if (self.repository.revisionList.grapher) { + PBGitGrapher* g = self.repository.revisionList.grapher; + [aCell setCellInfo: [g cellInfoForRow:rowIndex]]; + } } @end diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index a30ef63..b2841be 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ F5B721C40E05CF7E00AF29DC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F5B721C20E05CF7E00AF29DC /* MainMenu.xib */; }; F5DFFA6C0E075D8800617813 /* PBEasyFS.m in Sources */ = {isa = PBXBuildFile; fileRef = F5DFFA6B0E075D8800617813 /* PBEasyFS.m */; }; F5FF4E180E0829C20006317A /* PBGitRevList.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E170E0829C20006317A /* PBGitRevList.m */; }; + F5FF4E7A0E082E440006317A /* PBGitGrapher.m in Sources */ = {isa = PBXBuildFile; fileRef = F5FF4E790E082E440006317A /* PBGitGrapher.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -79,6 +80,8 @@ F5DFFA6B0E075D8800617813 /* PBEasyFS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBEasyFS.m; sourceTree = ""; }; F5FF4E160E0829C20006317A /* PBGitRevList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRevList.h; sourceTree = ""; }; F5FF4E170E0829C20006317A /* PBGitRevList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitRevList.m; sourceTree = ""; }; + F5FF4E780E082E440006317A /* PBGitGrapher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGrapher.h; sourceTree = ""; }; + F5FF4E790E082E440006317A /* PBGitGrapher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitGrapher.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -185,6 +188,8 @@ F56174540E05887E001DCD79 /* Git */ = { isa = PBXGroup; children = ( + F5FF4E780E082E440006317A /* PBGitGrapher.h */, + F5FF4E790E082E440006317A /* PBGitGrapher.m */, F5945E150E02B0C200706420 /* PBGitRepository.h */, F5945E160E02B0C200706420 /* PBGitRepository.m */, F56524EE0E02D45200F03B52 /* PBGitCommit.h */, @@ -317,6 +322,7 @@ F5DFFA6C0E075D8800617813 /* PBEasyFS.m in Sources */, F50FE0E30E07BE9600854FCD /* PBGitRevisionCell.m in Sources */, F5FF4E180E0829C20006317A /* PBGitRevList.m in Sources */, + F5FF4E7A0E082E440006317A /* PBGitGrapher.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -352,10 +358,10 @@ GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = GitTest_Prefix.pch; + GCC_PREFIX_HEADER = GitX_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; - PRODUCT_NAME = GitTest; + PRODUCT_NAME = GitX; WRAPPER_EXTENSION = app; ZERO_LINK = YES; }; diff --git a/PBGitGrapher.h b/PBGitGrapher.h new file mode 100644 index 0000000..97cfe09 --- /dev/null +++ b/PBGitGrapher.h @@ -0,0 +1,42 @@ +// +// PBGitGrapher.h +// GitX +// +// Created by Pieter de Bie on 17-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import "PBGitCommit.h" + + +struct PBGitGraphColumn { + NSString* commit; // Commit that we're looking for + int color; +}; + + +#define PBGitMaxColumns 10 + +struct PBGitGraphCellInfo { + struct PBGitGraphColumn columns[PBGitMaxColumns]; + int upperMapping[PBGitMaxColumns]; //How are the offsets compared to previous cell? + int lowerMapping[PBGitMaxColumns]; //How are the offsets compared to this cell? + int position; + NSString* commit; // Commit in cell + int numColumns; + int numNewColumns; +}; + +void add_commit_to_graph(struct PBGitGraphCellInfo* info, NSString* parent, int* mapping_index); + +typedef struct PBGitGraphCellInfo PBGitCellInfo; + + +@interface PBGitGrapher : NSObject { + PBGitCellInfo* cellsInfo; +} + +- (void) parseCommits: (NSArray *) array; +- (struct PBGitGraphCellInfo) cellInfoForRow: (int) row; +@end diff --git a/PBGitGrapher.m b/PBGitGrapher.m new file mode 100644 index 0000000..46a1004 --- /dev/null +++ b/PBGitGrapher.m @@ -0,0 +1,87 @@ +// +// PBGitGrapher.m +// GitX +// +// Created by Pieter de Bie on 17-06-08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "PBGitGrapher.h" +#import "PBGitCommit.h" + +@implementation PBGitGrapher +static NSColor** PBGitGraphColors; + +- (void) parseCommits: (NSArray *) commits +{ + cellsInfo = malloc(sizeof(struct PBGitGraphCellInfo) * [commits count]); + memset(cellsInfo, 0, sizeof(struct PBGitGraphCellInfo) * [commits count]); + + int row = 0; + + struct PBGitGraphCellInfo *previous = nil; + for (PBGitCommit* commit in commits) { + struct PBGitGraphCellInfo *info = &(cellsInfo[row]); + info->commit = commit.sha; + info->numColumns = 0; + + int i = 0, newPos = -1; + BOOL didFirst = NO; + // First, iterate over earlier columns and pass through any that don't want this commit + if (previous != nil) { + for (i = 0; i < 10; i++) { + if ((previous->columns[i].commit) == nil) + continue; + if ([previous->columns[i].commit isEqualToString:info->commit]) { + if (!didFirst) { + didFirst = YES; + info->position = info->numColumns++; + info->columns[info->position].commit = [commit.parents objectAtIndex:0]; + info->columns[info->position].color = previous->columns[i].color; + } + newPos = info->position; + } + else { + newPos = info->numColumns++; + info->columns[newPos] = previous->columns[i]; + if (newPos > 1) + info->columns[newPos].color = (info->columns[newPos - 1].color + 1) % 4; + } + + info->upperMapping[newPos] = i; + if (previous) + previous->lowerMapping[i] = newPos; + + } + } + + //Add your own parents! + BOOL doFirst = YES; + + for (NSString* parent in commit.parents) { + if (doFirst) { + doFirst = NO; + if (didFirst) + continue; + info->position = info->numColumns++; + info->columns[info->position].commit = parent; + continue; + } + + info->columns[info->numColumns++].commit = parent; + } + previous = info; + ++row; + } +} +- (struct PBGitGraphCellInfo) cellInfoForRow: (int) row +{ + return cellsInfo[row]; +} + +- (void) finalize +{ + free(cellsInfo); + [super finalize]; +} +@end diff --git a/PBGitRepository.m b/PBGitRepository.m index bd9264c..ff0b273 100644 --- a/PBGitRepository.m +++ b/PBGitRepository.m @@ -14,7 +14,7 @@ @implementation PBGitRepository -@synthesize path; +@synthesize path, revisionList; static NSString* gitPath; + (void) initialize diff --git a/PBGitRevList.h b/PBGitRevList.h index 635e60a..c601d4b 100644 --- a/PBGitRevList.h +++ b/PBGitRevList.h @@ -12,10 +12,13 @@ @interface PBGitRevList : NSObject { NSArray* commits; NSArray* parameters; + id grapher; id repository; } - initWithRepository:(id)repo andRevListParameters:(NSArray*) params; @property(retain) NSArray* commits; +@property(retain) id grapher; + @end diff --git a/PBGitRevList.m b/PBGitRevList.m index c8356aa..d50328b 100644 --- a/PBGitRevList.m +++ b/PBGitRevList.m @@ -9,10 +9,11 @@ #import "PBGitRevList.h" #import "PBGitRepository.h" #import "PBGitCommit.h" +#import "PBGitGrapher.h" @implementation PBGitRevList -@synthesize commits; +@synthesize commits, grapher; - initWithRepository: (id) repo andRevListParameters: (NSArray*) params { parameters = params; @@ -72,10 +73,16 @@ } [self performSelectorOnMainThread:@selector(setCommits:) withObject:newArray waitUntilDone:YES]; + + PBGitGrapher* g = [[PBGitGrapher alloc] init]; + [g parseCommits: self.commits]; + [self performSelectorOnMainThread:@selector(setGrapher:) withObject:g waitUntilDone:YES]; + [self performSelectorOnMainThread:@selector(setCommits:) withObject:newArray waitUntilDone:YES]; + NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start]; NSLog(@"Loaded %i commits in %f seconds", num, duration); - [NSThread exit]; } + @end diff --git a/PBGitRevisionCell.h b/PBGitRevisionCell.h index d0d047a..8523b9c 100644 --- a/PBGitRevisionCell.h +++ b/PBGitRevisionCell.h @@ -7,10 +7,12 @@ // #import +#import "PBGitGrapher.h" @interface PBGitRevisionCell : NSTextFieldCell { - NSNumber* commit; + PBGitCellInfo cellInfo; + BOOL isReady; } -@property(retain) NSNumber* commit; +@property(assign) PBGitCellInfo cellInfo; @end diff --git a/PBGitRevisionCell.m b/PBGitRevisionCell.m index 6028439..457eac3 100644 --- a/PBGitRevisionCell.m +++ b/PBGitRevisionCell.m @@ -10,15 +10,90 @@ @implementation PBGitRevisionCell -@synthesize commit; + +@synthesize cellInfo; +-(void) setCellInfo: (PBGitCellInfo) info +{ + isReady = YES; + cellInfo = info; +} + +- (id) initWithCoder: (id) coder +{ + self = [super initWithCoder:coder]; + if (self != nil) { + isReady = NO; + } + return self; +} + +- (void) drawLineForColumn: (int)c inRect: (NSRect) r +{ + NSArray* col = [NSArray arrayWithObjects:[NSColor redColor], [NSColor blueColor], + [NSColor orangeColor], [NSColor blackColor], [NSColor greenColor], nil]; + + int columnWidth = 10; + NSPoint origin = r.origin; + + NSPoint source; + NSPoint center; + NSPoint destination; + + center = NSMakePoint( origin.x + columnWidth * c, origin.y + r.size.height * 0.5); + + if (cellInfo.upperMapping[c] == cellInfo.position && c != cellInfo.upperMapping[c]) { + source = NSMakePoint(origin.x + columnWidth * cellInfo.upperMapping[c], origin.y + r.size.height * 0.5); + destination = NSMakePoint( origin.x + columnWidth * cellInfo.lowerMapping[c], origin.y + r.size.height); + } else if (cellInfo.lowerMapping[c] == cellInfo.position && c != cellInfo.lowerMapping[c]) { + source = NSMakePoint( origin.x + columnWidth * cellInfo.upperMapping[c], origin.y); + destination = NSMakePoint( origin.x + columnWidth * c, origin.y + r.size.height); + } else { + source = NSMakePoint( origin.x + columnWidth * cellInfo.upperMapping[c], origin.y); + destination = NSMakePoint( origin.x + columnWidth * cellInfo.lowerMapping[c], origin.y + r.size.height); + } + + [[col objectAtIndex:cellInfo.columns[c].color] set]; + + NSBezierPath * path = [NSBezierPath bezierPath]; + [path setLineWidth:2]; + + [path moveToPoint:source]; + [path lineToPoint: center]; + [path lineToPoint: destination]; + [path stroke]; + + +} + +- (void) drawCircleForColumn: (int) c inRect: (NSRect) r +{ + NSArray* col = [NSArray arrayWithObjects:[NSColor redColor], [NSColor blueColor], + [NSColor orangeColor], [NSColor blackColor], [NSColor greenColor], nil]; + + int columnWidth = 10; + NSPoint origin = r.origin; + NSPoint columnOrigin = { origin.x + columnWidth * c, origin.y}; + + NSRect oval = { columnOrigin.x - 5, columnOrigin.y + r.size.height * 0.5 - 5, 10, 10}; + + + NSBezierPath * path = [NSBezierPath bezierPath]; + path = [NSBezierPath bezierPathWithOvalInRect:oval]; + [[col objectAtIndex:cellInfo.columns[c].color] set]; + [path fill]; + + NSRect smallOval = { columnOrigin.x - 3, columnOrigin.y + r.size.height * 0.5 - 3, 6, 6}; + [[NSColor whiteColor] set]; + path = [NSBezierPath bezierPathWithOvalInRect:smallOval]; + [path fill]; +} - (void) drawWithFrame: (NSRect) rect inView:(NSView *)view { - - // Don't do all this drawing for now. - [super drawWithFrame:rect inView:view]; - return; - float pathWidth = 20; + if (!isReady) + return [super drawWithFrame:rect inView:view]; + + float pathWidth = 10 + 10 * cellInfo.numColumns; NSRect ownRect; NSDivideRect(rect, &ownRect, &rect, pathWidth, NSMinXEdge); @@ -26,42 +101,18 @@ // Adjust by removing the border ownRect.size.height += 2; ownRect.origin.y -= 1; + ownRect.origin.x += 10; - NSPoint origin = ownRect.origin; - NSPoint middle = { origin.x + pathWidth / 2, origin.y + ownRect.size.height * 0.5 }; - - [[NSColor redColor] set]; - NSBezierPath * path = [NSBezierPath bezierPath]; - [path moveToPoint:NSMakePoint(middle.x, origin.y)]; - [path setLineWidth:2]; - [path lineToPoint: NSMakePoint(middle.x, origin.y + ownRect.size.height)]; - [path stroke]; - [path setLineWidth:1]; - - - NSRect oval = { middle.x - 5, middle.y -5, 10, 10}; - [[NSColor orangeColor] set]; - path = [NSBezierPath bezierPathWithOvalInRect:oval]; - [path fill]; - - if ([self.commit intValue] == 0) - [[NSColor redColor] set]; - else - [[NSColor blueColor] set]; - - [path stroke]; - - NSRect smallOval = { middle.x - 3, middle.y - 3, 6, 6}; - [[NSColor whiteColor] set]; - path = [NSBezierPath bezierPathWithOvalInRect:smallOval]; - [path fill]; - [[NSColor blackColor] set]; - [path stroke]; + int column = 0; + for (column = 0; column < cellInfo.numColumns; column++) { + if (cellInfo.columns[column].color >= 0) + [self drawLineForColumn: column inRect: ownRect]; + } + [self drawCircleForColumn: cellInfo.position inRect: ownRect]; [super drawWithFrame:rect inView:view]; - [[NSColor blueColor] set]; - //[path stroke]; + isReady = NO; } @end From df490103f6f48ad0c43b50b68285ad2372030a53 Mon Sep 17 00:00:00 2001 From: Pieter de Bie Date: Wed, 18 Jun 2008 13:14:40 +0200 Subject: [PATCH 2/5] PBGitGrapher: Improve the grapher This takes care of most situations. However, we don't have enough space in the upperMapping to display multiple lines. Therefore we need to improve it later on. --- PBGitGrapher.h | 2 +- PBGitGrapher.m | 105 ++++++++++++++++++++++++++++++++------------ PBGitRevisionCell.m | 46 +++++++++---------- 3 files changed, 99 insertions(+), 54 deletions(-) diff --git a/PBGitGrapher.h b/PBGitGrapher.h index 97cfe09..3a421f2 100644 --- a/PBGitGrapher.h +++ b/PBGitGrapher.h @@ -16,7 +16,7 @@ struct PBGitGraphColumn { }; -#define PBGitMaxColumns 10 +#define PBGitMaxColumns 100 struct PBGitGraphCellInfo { struct PBGitGraphColumn columns[PBGitMaxColumns]; diff --git a/PBGitGrapher.m b/PBGitGrapher.m index 46a1004..9a17777 100644 --- a/PBGitGrapher.m +++ b/PBGitGrapher.m @@ -10,7 +10,7 @@ #import "PBGitCommit.h" @implementation PBGitGrapher -static NSColor** PBGitGraphColors; + - (void) parseCommits: (NSArray *) commits { @@ -26,50 +26,101 @@ static NSColor** PBGitGraphColors; info->numColumns = 0; int i = 0, newPos = -1; + for (i = 0; i < PBGitMaxColumns; i++) { + info->lowerMapping[i] = -1; + info->upperMapping[i] = -1; + } + BOOL didFirst = NO; // First, iterate over earlier columns and pass through any that don't want this commit if (previous != nil) { - for (i = 0; i < 10; i++) { + + // We can't count until numColumns here, as it's only used for the width of the cell. + for (i = 0; i < PBGitMaxColumns; i++) { if ((previous->columns[i].commit) == nil) continue; + + // This is our commit! We should do a "merge": move the line from + // our upperMapping to their lowerMapping if ([previous->columns[i].commit isEqualToString:info->commit]) { if (!didFirst) { didFirst = YES; info->position = info->numColumns++; info->columns[info->position].commit = [commit.parents objectAtIndex:0]; - info->columns[info->position].color = previous->columns[i].color; } newPos = info->position; + info->upperMapping[i] = newPos; } - else { - newPos = info->numColumns++; - info->columns[newPos] = previous->columns[i]; - if (newPos > 1) - info->columns[newPos].color = (info->columns[newPos - 1].color + 1) % 4; + else { + // We are not this commit. + // Try to find an earlier column for this commit. + int j; + BOOL found = NO; + for (j = 0; j < info->numColumns; j++) { + if (j == info->position) + continue; + if ([previous->columns[i].commit isEqualToString: info->columns[j].commit]) { + // We already have a column for this commit. use it instead + newPos = j; + info->upperMapping[previous->lowerMapping[i]] = newPos; + found = YES; + break; + } + } + // We need a new column for this. + if (!found) { + newPos = info->numColumns++; + info->columns[newPos] = previous->columns[i]; + info->upperMapping[newPos] = newPos; + } } - - info->upperMapping[newPos] = i; - if (previous) - previous->lowerMapping[i] = newPos; - + // For existing columns, we always just continue straight down + info->lowerMapping[newPos] = newPos; } } - //Add your own parents! - BOOL doFirst = YES; - - for (NSString* parent in commit.parents) { - if (doFirst) { - doFirst = NO; - if (didFirst) - continue; - info->position = info->numColumns++; - info->columns[info->position].commit = parent; - continue; - } - - info->columns[info->numColumns++].commit = parent; + //Add your own parents + + // If we already did the first parent, don't do so again + if (!didFirst) { + info->position = info->numColumns++; + info->columns[info->position].commit = [commit.parents objectAtIndex:0]; + info->lowerMapping[info->position] = info->position; } + + // Add all other parents + + // If we add at least one parent, we can go back a single column. + // This boolean will tell us if that happened + BOOL addedParent = NO; + + for (NSString* parent in [commit.parents subarrayWithRange:NSMakeRange(1, [commit.parents count] -1)]) { + int i; + BOOL was_displayed = NO; + for (i = 0; i < info->numColumns; i++) + if ([info->columns[i].commit isEqualToString: parent]) { + // TODO! + // !!! BUG + // This overwrites an existing mapping. + // We should instead have the possibility + // to add multiple lower mappings + // As we don't have that now, pieces of the graph are missing + info->lowerMapping[i] = info->position; + was_displayed = YES; + break; + } + if (was_displayed) + continue; + + // Really add this parent + addedParent = YES; + info->columns[info->numColumns++].commit = parent; + info->lowerMapping[info->numColumns -1] = info->position; + } + + // A parent was added, so we have room to not indent. + if (addedParent) + info->numColumns--; previous = info; ++row; } diff --git a/PBGitRevisionCell.m b/PBGitRevisionCell.m index 457eac3..3d960e2 100644 --- a/PBGitRevisionCell.m +++ b/PBGitRevisionCell.m @@ -27,42 +27,31 @@ return self; } -- (void) drawLineForColumn: (int)c inRect: (NSRect) r +- (NSArray*) colors { - NSArray* col = [NSArray arrayWithObjects:[NSColor redColor], [NSColor blueColor], + return [NSArray arrayWithObjects:[NSColor redColor], [NSColor blueColor], [NSColor orangeColor], [NSColor blackColor], [NSColor greenColor], nil]; +} + +- (void) drawLineFromColumn: (int) from toColumn: (int) to inRect: (NSRect) r offset: (int) offset +{ int columnWidth = 10; NSPoint origin = r.origin; - NSPoint source; - NSPoint center; - NSPoint destination; - - center = NSMakePoint( origin.x + columnWidth * c, origin.y + r.size.height * 0.5); + NSPoint source = NSMakePoint(origin.x + columnWidth* from, origin.y + offset); + NSPoint center = NSMakePoint( origin.x + columnWidth * to, origin.y + r.size.height * 0.5); - if (cellInfo.upperMapping[c] == cellInfo.position && c != cellInfo.upperMapping[c]) { - source = NSMakePoint(origin.x + columnWidth * cellInfo.upperMapping[c], origin.y + r.size.height * 0.5); - destination = NSMakePoint( origin.x + columnWidth * cellInfo.lowerMapping[c], origin.y + r.size.height); - } else if (cellInfo.lowerMapping[c] == cellInfo.position && c != cellInfo.lowerMapping[c]) { - source = NSMakePoint( origin.x + columnWidth * cellInfo.upperMapping[c], origin.y); - destination = NSMakePoint( origin.x + columnWidth * c, origin.y + r.size.height); - } else { - source = NSMakePoint( origin.x + columnWidth * cellInfo.upperMapping[c], origin.y); - destination = NSMakePoint( origin.x + columnWidth * cellInfo.lowerMapping[c], origin.y + r.size.height); - } + // Just use red for now. + [[[self colors] objectAtIndex:0] set]; - [[col objectAtIndex:cellInfo.columns[c].color] set]; - NSBezierPath * path = [NSBezierPath bezierPath]; [path setLineWidth:2]; - [path moveToPoint:source]; + [path moveToPoint: source]; [path lineToPoint: center]; - [path lineToPoint: destination]; [path stroke]; - } - (void) drawCircleForColumn: (int) c inRect: (NSRect) r @@ -104,12 +93,17 @@ ownRect.origin.x += 10; int column = 0; - for (column = 0; column < cellInfo.numColumns; column++) { - if (cellInfo.columns[column].color >= 0) - [self drawLineForColumn: column inRect: ownRect]; + + // We can't iterate over numColumns here, as there may be connections to be drawn outside our columns. + for (column = 0; column < PBGitMaxColumns; column++) { + if (cellInfo.upperMapping[column] !=-1) + [self drawLineFromColumn:column toColumn: cellInfo.upperMapping[column] inRect:ownRect offset: 0]; + if (cellInfo.lowerMapping[column] !=-1) + [self drawLineFromColumn: column toColumn: cellInfo.lowerMapping[column] inRect:ownRect offset: ownRect.size.height]; } + [self drawCircleForColumn: cellInfo.position inRect: ownRect]; - + [super drawWithFrame:rect inView:view]; isReady = NO; From c55c916ace8353070026a938f20d4c71ec9d6f00 Mon Sep 17 00:00:00 2001 From: Pieter de Bie Date: Wed, 18 Jun 2008 13:15:14 +0200 Subject: [PATCH 3/5] PBGitRevList: use --topo-order for better graphs --topo-order makes sure that commits are output in a saner way. This results in cleaner graphs. --- PBGitRevList.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PBGitRevList.m b/PBGitRevList.m index 77e7519..5111a2e 100644 --- a/PBGitRevList.m +++ b/PBGitRevList.m @@ -28,7 +28,7 @@ NSMutableArray * newArray = [NSMutableArray array]; NSDate* start = [NSDate date]; - NSFileHandle* handle = [repository handleForCommand:@"log --pretty=format:%H\01%an\01%s\01%P\01%at HEAD"]; + NSFileHandle* handle = [repository handleForCommand:@"log --topo-order --pretty=format:%H\01%an\01%s\01%P\01%at HEAD"]; int fd = [handle fileDescriptor]; FILE* f = fdopen(fd, "r"); From 51b0e7b0eb28e32701ecf548464a1717920cbfae Mon Sep 17 00:00:00 2001 From: Pieter de Bie Date: Wed, 18 Jun 2008 16:05:15 +0200 Subject: [PATCH 4/5] Discard branches that run for too long --- PBGitGrapher.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PBGitGrapher.m b/PBGitGrapher.m index 9a17777..e643807 100644 --- a/PBGitGrapher.m +++ b/PBGitGrapher.m @@ -69,8 +69,13 @@ } // We need a new column for this. if (!found) { + + if (previous->columns[i].color == 10) + continue; + newPos = info->numColumns++; info->columns[newPos] = previous->columns[i]; + info->columns[newPos].color++; info->upperMapping[newPos] = newPos; } } From 061eb7de48526f13306d1108452db77ed54047c1 Mon Sep 17 00:00:00 2001 From: Pieter de Bie Date: Sat, 21 Jun 2008 13:41:55 +0200 Subject: [PATCH 5/5] Grapher: add speed tests This creates a new directory speed_test with different implementations that we can use to power the grapher. The current implementation is somewhat lacking in that we build up the graph completely at the end. If we can do it while traversing the tree, we can build and display it earlier. Similarly, if we can leave out the line calculation but just work with the columns, we save computation at load time, and can defer the lines to the drawing process. That will also clear up the problem of not being able to draw some lines. --- speed_test/README | 26 +++++++++++++++++++++ speed_test/array.m | 33 +++++++++++++++++++++++++++ speed_test/global.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ speed_test/malloc.c | 48 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 speed_test/README create mode 100644 speed_test/array.m create mode 100644 speed_test/global.c create mode 100644 speed_test/malloc.c diff --git a/speed_test/README b/speed_test/README new file mode 100644 index 0000000..205aef6 --- /dev/null +++ b/speed_test/README @@ -0,0 +1,26 @@ +These tests demonstrate 3 different ways to allocate memory for the graph +viewer. + +The methods: + +1. global + This method allocates a global memory pool that is used by all structs. + It is the fastest method and should be easy to clean up. You do have to + make sure that any pointers to the memory used by others is cleaned up. +1. malloc + This methods does two mallocs for every iteration. It is slightly slower + (2x as slow), but won't require as much unfragmented memory. It is harder + to clean up this memory, as it requires an equal amount of free's. +2. array + This method uses NSMutableArray's to store the necessary information. It is + by far the slowest (10x slower than global) but will make use of + Objective-C's garbage collection. This is the easiest way to go if it isn't + too slow. Looping and creating the arrays takes about 2 seconds for 800k + iterations. The question is if this significantly slows down the work. + +Results: + + global: 0.18 seconds + malloc: 0.39 seconds + array: 1.90 seconds + diff --git a/speed_test/array.m b/speed_test/array.m new file mode 100644 index 0000000..58942dc --- /dev/null +++ b/speed_test/array.m @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() { + srandomdev(); + + int i = 0; struct list* last; + int num = atoi("8000000"); + + int size = 1000; + int totColumns = 10000; + int currentColumn = 0; + + NSMutableArray* array = [NSMutableArray arrayWithCapacity: 100*size]; + + for (i = 0; i < num; i++) { + int numColumns = i % 5; + + NSMutableArray* arr = [NSMutableArray arrayWithCapacity: numColumns]; + int j; + for (j = 0; j < numColumns; j++) + [arr addObject: @"Ha"]; + [array addObject: arr]; + } + + [array release]; + return 0; +} \ No newline at end of file diff --git a/speed_test/global.c b/speed_test/global.c new file mode 100644 index 0000000..f63068a --- /dev/null +++ b/speed_test/global.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +struct list { + void* columns; + int numColumns; +}; +struct hash { + char value[40]; +}; + +int main() { + srandomdev(); + + int i = 0; struct list* last; + int num = atoi("8000000"); + + int size = 1000; + int totColumns = 10000; + int currentColumn = 0; + + /* Initialize initial list of revisions */ + struct list* revisionList = malloc(size * sizeof(struct list)); + struct hash* columns = malloc(totColumns * sizeof(struct hash)); + + struct hash standardColumn; + strcpy(standardColumn.value, "Haha pieter"); + for (i = 0; i < num; i++) { + if (size <= i) { + size *= 2; + revisionList = realloc(revisionList, size * sizeof(struct list)); + } + + struct list* a = revisionList + i; + a->numColumns = i % 5; + if (currentColumn + a->numColumns > totColumns) { + totColumns *= 2; + printf("Reallocing columns. New total: %i\n", totColumns); + columns = realloc(columns, totColumns * sizeof(struct hash)); + } + int j; + for (j = 0; j < a->numColumns; j++) { + //ccolumns[currentColumn++] = st + strncpy(columns[currentColumn++].value, "Haha pieter is cool", 20); + } + } + + printf("Num value at 3000 is: %i vs %i\n", revisionList[3000].numColumns, (int) (5 * random())); + printf("Value of 1000'd column is: %s\n", columns[1000].value); + sleep(5); + return 0; +} \ No newline at end of file diff --git a/speed_test/malloc.c b/speed_test/malloc.c new file mode 100644 index 0000000..5fbadca --- /dev/null +++ b/speed_test/malloc.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include + +struct list { + struct hash* columns; + int numColumns; +}; +struct hash { + char value[40]; +}; + +int main() { + srandomdev(); + + int i = 0; struct list* last; + int num = atoi("8000000"); + + int size = 1000; + /* Initialize initial list of revisions */ + struct list** revisionList = malloc(size * sizeof(struct list*)); + + struct hash standardColumn; + strcpy(standardColumn.value, "Haha pieter"); + for (i = 0; i < num; i++) { + if (size <= i) { + size *= 2; + revisionList = realloc(revisionList, size * sizeof(struct list*)); + } + + struct list* a = malloc(sizeof(struct list)); + revisionList[i] = a; + + a->numColumns = i % 5; + a->columns = malloc(a->numColumns * sizeof(struct hash)); + int j; + for (j = 0; j < a->numColumns; j++) { + //ccolumns[currentColumn++] = st + strncpy(a->columns[j].value, "Haha pieter is cool", 20); + } + } + + printf("Num value at 3000 is: %i vs %i\n", revisionList[3000]->numColumns, (int) (5 * random())); + return 0; +} \ No newline at end of file