Skip to content

Commit d9df533

Browse files
committed
Guard against stale PBGitRevList parses overlapping (#563)
A burst of git-directory FSEvents (e.g. from `git switch -c <branch>` touching both a ref file and HEAD) can trigger overlapping loadRevisionsWithCompletionBlock: calls on the same PBGitRevList instance. The previous parse operation's trailing commit-batch flush is fire-and-forget and can still land after a newer parse has already reset/appended its own commits, since NSOperation.isCancelled is only checked cooperatively and can lose that race. The result is duplicate commits appended into the history list. Track a per-instance load generation and reject any updateCommits: flush whose generation no longer matches the current one, closing the race regardless of operation-queue/FSEvents timing.
1 parent 8951252 commit d9df533

1 file changed

Lines changed: 10 additions & 6 deletions

File tree

‎Classes/git/PBGitRevList.m‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ @interface PBGitRevList ()
3131

3232
@property (nonatomic, strong) NSOperationQueue *operationQueue;
3333

34+
@property (nonatomic, assign) NSUInteger loadGeneration;
35+
3436
@end
3537

3638

@@ -62,6 +64,8 @@ - (void)loadRevisionsWithCompletionBlock:(void (^)(void))completionBlock
6264

6365
self.resetCommits = YES;
6466

67+
NSUInteger generation = ++self.loadGeneration;
68+
6569
NSBlockOperation *parseOperation = [[NSBlockOperation alloc] init];
6670

6771
__weak typeof(self) weakSelf = self;
@@ -75,7 +79,7 @@ - (void)loadRevisionsWithCompletionBlock:(void (^)(void))completionBlock
7579
GTEnumerator *enu = [[GTEnumerator alloc] initWithRepository:repo error:&error];
7680

7781
[weakSelf setupEnumerator:enu forRevspec:weakSelf.currentRev];
78-
[weakSelf addCommitsFromEnumerator:enu operation:weakParseOperation];
82+
[weakSelf addCommitsFromEnumerator:enu operation:weakParseOperation generation:generation];
7983
}];
8084
[parseOperation setCompletionBlock:completionBlock];
8185

@@ -94,9 +98,9 @@ - (BOOL)isParsing
9498
}
9599

96100

97-
- (void)updateCommits:(NSArray<PBGitCommit *> *)revisions operation:(NSOperation *)operation
101+
- (void)updateCommits:(NSArray<PBGitCommit *> *)revisions operation:(NSOperation *)operation generation:(NSUInteger)generation
98102
{
99-
if (!revisions || [revisions count] == 0 || operation.cancelled)
103+
if (!revisions || [revisions count] == 0 || operation.cancelled || generation != self.loadGeneration)
100104
return;
101105

102106
if (self.resetCommits) {
@@ -193,7 +197,7 @@ - (void)setupEnumerator:(GTEnumerator *)enumerator
193197
}
194198
}
195199

196-
- (void)addCommitsFromEnumerator:(GTEnumerator *)enumerator operation:(NSOperation *)operation
200+
- (void)addCommitsFromEnumerator:(GTEnumerator *)enumerator operation:(NSOperation *)operation generation:(NSUInteger)generation
197201
{
198202
PBGitGrapher *g = [[PBGitGrapher alloc] init];
199203
__block NSDate *lastUpdate = [NSDate date];
@@ -248,7 +252,7 @@ - (void)addCommitsFromEnumerator:(GTEnumerator *)enumerator operation:(NSOperati
248252
NSArray<PBGitCommit *> *updatedRevisions = [revisions copy];
249253

250254
dispatch_async(dispatch_get_main_queue(), ^{
251-
[self updateCommits:updatedRevisions operation:operation];
255+
[self updateCommits:updatedRevisions operation:operation generation:generation];
252256
});
253257

254258
[revisions removeAllObjects];
@@ -266,7 +270,7 @@ - (void)addCommitsFromEnumerator:(GTEnumerator *)enumerator operation:(NSOperati
266270
NSArray<PBGitCommit *> *updatedRevisions = [revisions copy];
267271

268272
dispatch_async(dispatch_get_main_queue(), ^{
269-
[self updateCommits:updatedRevisions operation:operation];
273+
[self updateCommits:updatedRevisions operation:operation generation:generation];
270274
});
271275
}
272276

0 commit comments

Comments
 (0)