diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 59d0476..609efee 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -274,8 +274,6 @@ isa = PBXGroup; children = ( F5C6F6750E65FE2B00478D97 /* Graphing */, - F5FF4E780E082E440006317A /* PBGitGrapher.h */, - F5FF4E790E082E440006317A /* PBGitGrapher.m */, F5945E150E02B0C200706420 /* PBGitRepository.h */, F5945E160E02B0C200706420 /* PBGitRepository.m */, F56524EE0E02D45200F03B52 /* PBGitCommit.h */, @@ -342,6 +340,8 @@ F5C6F6750E65FE2B00478D97 /* Graphing */ = { isa = PBXGroup; children = ( + F5FF4E780E082E440006317A /* PBGitGrapher.h */, + F5FF4E790E082E440006317A /* PBGitGrapher.m */, F5C007730E731B48007B84B2 /* PBGitRef.h */, F5C007740E731B48007B84B2 /* PBGitRef.m */, F50FE0E10E07BE9600854FCD /* PBGitRevisionCell.h */, diff --git a/PBGitGrapher.h b/PBGitGrapher.h index 2b46430..a4a0d07 100644 --- a/PBGitGrapher.h +++ b/PBGitGrapher.h @@ -20,11 +20,14 @@ struct PBGitGraphColumn { #define PBGitMaxColumns 100 @interface PBGitGrapher : NSObject { + PBGraphCellInfo* previous; + NSMutableArray* previousLanes; NSMutableArray* cellsInfo; + NSDictionary* refs; PBGitRepository* repository; } - (id) initWithRepository: (PBGitRepository*) repo; -- (void) parseCommits: (NSArray *) array; +- (void) decorateCommit: (PBGitCommit *) commit; - (PBGraphCellInfo*) cellInfoForRow: (int) row; @end diff --git a/PBGitGrapher.m b/PBGitGrapher.m index 5dbcd46..da4794e 100644 --- a/PBGitGrapher.m +++ b/PBGitGrapher.m @@ -14,139 +14,127 @@ - (id) initWithRepository: (PBGitRepository*) repo { - repository = repo; + refs = repo.refs; + // We don't know how many commits to parse. + cellsInfo = [NSMutableArray arrayWithCapacity:100]; + + previousLanes = [NSMutableArray array]; + return self; } -- (void) parseCommits: (NSArray *) commits +- (void) decorateCommit: (PBGitCommit *) commit { - cellsInfo = [NSMutableArray arrayWithCapacity: [commits count]]; - int row = 0; + int i = 0, newPos = -1; + NSMutableArray* currentLanes = [NSMutableArray array]; + NSMutableArray* lines = [NSMutableArray array]; + PBGitLane* currentLane = NULL; + BOOL didFirst = NO; - NSDictionary* refs = nil; - if (repository) - refs = repository.refs; + // First, iterate over earlier columns and pass through any that don't want this commit + if (previous != nil) { - PBGraphCellInfo* previous; - NSMutableArray* previousLanes = [NSMutableArray array]; - - for (PBGitCommit* commit in commits) { - int i = 0, newPos = -1; - NSMutableArray* currentLanes = [NSMutableArray array]; - NSMutableArray* lines = [NSMutableArray array]; - PBGitLane* currentLane = NULL; - BOOL didFirst = NO; - - // First, iterate over earlier columns and pass through any that don't want this commit - if (previous != nil) { - - // We can't count until numColumns here, as it's only used for the width of the cell. - for (PBGitLane* lane in previousLanes) { - i++; - // This is our commit! We should do a "merge": move the line from - // our upperMapping to their lowerMapping - if ([lane isCommit:commit.sha]) { - if (!didFirst) { - didFirst = YES; - currentLane = lane; - [currentLanes addObject: lane]; - newPos = [currentLanes count]; - } - [lines addObject: [PBGitGraphLine upperLineFrom: i to: newPos color: [lane index]]]; + // We can't count until numColumns here, as it's only used for the width of the cell. + for (PBGitLane* lane in previousLanes) { + i++; + // This is our commit! We should do a "merge": move the line from + // our upperMapping to their lowerMapping + if ([lane isCommit:commit.sha]) { + if (!didFirst) { + didFirst = YES; + currentLane = lane; + [currentLanes addObject: lane]; + newPos = [currentLanes count]; } - else { - // We are not this commit. - // Try to find an earlier column for this commit. - int j = 0; - BOOL found = NO; - for (PBGitLane* column in currentLanes) { - j++; - // ??? what is this? + [lines addObject: [PBGitGraphLine upperLineFrom: i to: newPos color: [lane index]]]; + } + else { + // We are not this commit. + // Try to find an earlier column for this commit. + int j = 0; + BOOL found = NO; + for (PBGitLane* column in currentLanes) { + j++; + // ??? what is this? // if (j == newPos) // continue; - if ([lane isCommit: commit.sha]) { - // We already have a column for this commit. use it instead - [lines addObject: [PBGitGraphLine upperLineFrom: i to: j color: [lane index]]]; - found = YES; - break; - } - } - - // We need a new column for this. - if (!found) { - - // This was used as a hack to stop large lanes from drawing - //if (previous->columns[i].color == 10) - // continue; - - [currentLanes addObject: lane]; - [lines addObject: [PBGitGraphLine upperLineFrom: i to: [currentLanes count] color: [lane index]]]; - [lines addObject: [PBGitGraphLine lowerLineFrom: [currentLanes count] to: [currentLanes count] color: [lane index]]]; + if ([lane isCommit: commit.sha]) { + // We already have a column for this commit. use it instead + [lines addObject: [PBGitGraphLine upperLineFrom: i to: j color: [lane index]]]; + found = YES; + break; } } - // For existing columns, we always just continue straight down - // ^^ I don't know what that means anymore :( - [lines addObject:[PBGitGraphLine lowerLineFrom:newPos to:newPos color: [currentLane index]]]; - } - } - - //Add your own parents - - // If we already did the first parent, don't do so again - if (!didFirst) { - PBGitLane* newLane = [[PBGitLane alloc] initWithCommit:[commit.parents objectAtIndex:0]]; - [currentLanes addObject: newLane]; - newPos = [currentLanes count]; - [lines addObject:[PBGitGraphLine lowerLineFrom: newPos to: newPos color: [newLane index]]]; - } - - // 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 = 0; - BOOL was_displayed = NO; - for (PBGitLane* column in currentLanes) { - i++; - if ([column isCommit: parent]) { - [lines addObject:[PBGitGraphLine lowerLineFrom: i to: newPos color: [column index]]]; - was_displayed = YES; - break; + // We need a new column for this. + if (!found) { + + [currentLanes addObject: lane]; + [lines addObject: [PBGitGraphLine upperLineFrom: i to: [currentLanes count] color: [lane index]]]; + [lines addObject: [PBGitGraphLine lowerLineFrom: [currentLanes count] to: [currentLanes count] color: [lane index]]]; } } - if (was_displayed) - continue; - - // Really add this parent - addedParent = YES; - PBGitLane* newLane = [[PBGitLane alloc] initWithCommit:parent]; - [currentLanes addObject: newLane]; - [lines addObject:[PBGitGraphLine lowerLineFrom: [currentLanes count] to: newPos color: [newLane index]]]; + // For existing columns, we always just continue straight down + // ^^ I don't know what that means anymore :( + [lines addObject:[PBGitGraphLine lowerLineFrom:newPos to:newPos color: [currentLane index]]]; } - - ++row; - previous = [[PBGraphCellInfo alloc] initWithPosition:newPos andLines:lines]; - previous.sign = commit.sign; - if (refs && [refs objectForKey:commit.sha]) - previous.refs = [refs objectForKey:commit.sha]; - - // If a parent was added, we have room to not indent. - if (addedParent) - previous.numColumns = [currentLanes count] - 1; - else - previous.numColumns = [currentLanes count]; - - if ([commit.parents count] > 0 && ![[commit.parents objectAtIndex:0] isEqualToString:@""]) - currentLane.sha = [commit.parents objectAtIndex:0]; - else - [currentLanes removeObject:currentLane]; - - previousLanes = currentLanes; - [cellsInfo addObject: previous]; } + + //Add your own parents + + // If we already did the first parent, don't do so again + if (!didFirst) { + PBGitLane* newLane = [[PBGitLane alloc] initWithCommit:[commit.parents objectAtIndex:0]]; + [currentLanes addObject: newLane]; + newPos = [currentLanes count]; + [lines addObject:[PBGitGraphLine lowerLineFrom: newPos to: newPos color: [newLane index]]]; + } + + // 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 = 0; + BOOL was_displayed = NO; + for (PBGitLane* column in currentLanes) { + i++; + if ([column isCommit: parent]) { + [lines addObject:[PBGitGraphLine lowerLineFrom: i to: newPos color: [column index]]]; + was_displayed = YES; + break; + } + } + if (was_displayed) + continue; + + // Really add this parent + addedParent = YES; + PBGitLane* newLane = [[PBGitLane alloc] initWithCommit:parent]; + [currentLanes addObject: newLane]; + [lines addObject:[PBGitGraphLine lowerLineFrom: [currentLanes count] to: newPos color: [newLane index]]]; + } + + previous = [[PBGraphCellInfo alloc] initWithPosition:newPos andLines:lines]; + previous.sign = commit.sign; + if (refs && [refs objectForKey:commit.sha]) + previous.refs = [refs objectForKey:commit.sha]; + + // If a parent was added, we have room to not indent. + if (addedParent) + previous.numColumns = [currentLanes count] - 1; + else + previous.numColumns = [currentLanes count]; + + if ([commit.parents count] > 0 && ![[commit.parents objectAtIndex:0] isEqualToString:@""]) + currentLane.sha = [commit.parents objectAtIndex:0]; + else + [currentLanes removeObject:currentLane]; + + previousLanes = currentLanes; + [cellsInfo addObject: previous]; } - (PBGraphCellInfo*) cellInfoForRow: (int) row @@ -156,7 +144,6 @@ - (void) finalize { - free(cellsInfo); [super finalize]; } @end diff --git a/PBGitRevList.m b/PBGitRevList.m index 8bdc268..14a7b22 100644 --- a/PBGitRevList.m +++ b/PBGitRevList.m @@ -65,7 +65,6 @@ NSMutableArray* newArray = [NSMutableArray array]; NSMutableArray* arguments; - NSDate* start = [NSDate date]; BOOL showSign = [rev hasLeftRight]; if (showSign) @@ -80,6 +79,10 @@ NSFileHandle* handle = [repository handleForArguments: arguments]; + // We decorate the commits in a separate thread. + NSThread * decorationThread = [[NSThread alloc] initWithTarget: self selector: @selector(decorateRevisions:) object:newArray]; + [decorationThread start]; + int fd = [handle fileDescriptor]; FILE* f = fdopen(fd, "r"); int BUFFERSIZE = 2048; @@ -87,7 +90,7 @@ buffer[BUFFERSIZE - 2] = 0; char* l; - int num = 0; + NSMutableString* currentLine = [NSMutableString string]; while (l = fgets(buffer, BUFFERSIZE, f)) { NSString *s = [NSString stringWithCString:(const char *)l encoding:NSUTF8StringEncoding]; @@ -117,28 +120,49 @@ if (showSign) newCommit.sign = [[components objectAtIndex:5] characterAtIndex:0]; - [newArray addObject: newCommit]; - num++; - if (num % 10000 == 0) - [self performSelectorOnMainThread:@selector(setCommits:) withObject:newArray waitUntilDone:NO]; + @synchronized(newArray) { + [newArray addObject: newCommit]; + } currentLine = [NSMutableString string]; } - [self performSelectorOnMainThread:@selector(setCommits:) withObject:newArray waitUntilDone:YES]; + [decorationThread cancel]; - if (![rev hasPathLimiter]) { - PBGitGrapher* g = [[PBGitGrapher alloc] initWithRepository: repository]; - [g parseCommits: self.commits]; - [self performSelectorOnMainThread:@selector(setGrapher:) withObject:g waitUntilDone:YES]; - [self performSelectorOnMainThread:@selector(setCommits:) withObject:newArray waitUntilDone:YES]; - } - else - grapher = nil; - - NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start]; - NSLog(@"Loaded %i commits in %f seconds", num, duration); [NSThread exit]; } +- (void) decorateRevisions: (NSMutableArray*) revisions +{ + NSDate* start = [NSDate date]; + + NSMutableArray* allRevisions = [NSMutableArray arrayWithCapacity:1000]; + int num = 0; + PBGitGrapher* g = [[PBGitGrapher alloc] initWithRepository: repository]; + [self performSelectorOnMainThread:@selector(setGrapher:) withObject:g waitUntilDone:YES]; + + while (!([[NSThread currentThread] isCancelled] && [revisions count] == 0)) { + if ([revisions count] == 0) + usleep(10000); + + NSArray* currentRevisions; + @synchronized(revisions) { + currentRevisions = [revisions copy]; + [revisions removeAllObjects]; + } + for (PBGitCommit* commit in currentRevisions) { + num++; + [g decorateCommit: commit]; + [allRevisions addObject: commit]; + if (num % 1000 == 0) + [self performSelectorOnMainThread:@selector(setCommits:) withObject:allRevisions waitUntilDone:NO]; + } + } + + [self performSelectorOnMainThread:@selector(setCommits:) withObject:allRevisions waitUntilDone:YES]; + NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start]; + NSLog(@"Loaded %i commits in %f seconds", num, duration); + + [NSThread exit]; +} @end