17
17
NSString *kPBGitRepositoryEventTypeUserInfoKey = @" kPBGitRepositoryEventTypeUserInfoKey" ;
18
18
NSString *kPBGitRepositoryEventPathsUserInfoKey = @" kPBGitRepositoryEventPathsUserInfoKey" ;
19
19
20
+
20
21
@interface PBGitRepositoryWatcher ()
21
22
22
23
@property (nonatomic , strong ) NSMutableDictionary *statusCache;
23
24
24
- - (void ) _handleEventCallback : (NSArray *)eventPaths ;
25
+ - (void ) handleGitDirEventCallback : (NSArray *)eventPaths ;
26
+ - (void ) handleWorkDirEventCallback : (NSArray *)eventPaths ;
27
+
25
28
@end
26
29
27
30
void PBGitRepositoryWatcherCallback (ConstFSEventStreamRef streamRef,
@@ -30,22 +33,20 @@ void PBGitRepositoryWatcherCallback(ConstFSEventStreamRef streamRef,
30
33
void *_eventPaths,
31
34
const FSEventStreamEventFlags eventFlags[],
32
35
const FSEventStreamEventId eventIds[]){
33
- PBGitRepositoryWatcher *watcher = (__bridge PBGitRepositoryWatcher*)clientCallBackInfo;
36
+ PBGitRepositoryWatcherCallbackBlock block = (__bridge PBGitRepositoryWatcherCallbackBlock)clientCallBackInfo;
37
+
34
38
NSMutableArray *changePaths = [[NSMutableArray alloc ] init ];
35
39
NSArray *eventPaths = (__bridge NSArray *)_eventPaths;
36
40
for (int i = 0 ; i < numEvents; ++i) {
37
41
NSString *path = [eventPaths objectAtIndex: i];
38
- if ([path hasSuffix: @" .lock" ]) {
39
- continue ;
40
- }
41
42
PBGitRepositoryWatcherEventPath *ep = [[PBGitRepositoryWatcherEventPath alloc ] init ];
42
- ep.path = path;
43
+ ep.path = [ path stringByStandardizingPath ] ;
43
44
ep.flag = eventFlags[i];
44
45
[changePaths addObject: ep];
45
-
46
+
46
47
}
47
- if (changePaths.count ) {
48
- [watcher _handleEventCallback: changePaths] ;
48
+ if (block && changePaths.count ) {
49
+ block ( changePaths) ;
49
50
}
50
51
}
51
52
@@ -55,39 +56,79 @@ @implementation PBGitRepositoryWatcher
55
56
56
57
- (id ) initWithRepository : (PBGitRepository *)theRepository {
57
58
self = [super init ];
58
- if (!self)
59
+ if (!self) {
59
60
return nil ;
60
-
61
+ }
62
+
63
+ __weak PBGitRepositoryWatcher* weakSelf = self;
61
64
repository = theRepository;
62
- FSEventStreamContext context = {0 , (__bridge void *)(self), NULL , NULL , NULL };
63
65
64
- NSString *indexPath = repository.gtRepo .gitDirectoryURL .path ;
65
- if (!indexPath) {
66
- return nil ;
66
+ {
67
+ gitDir = [repository.gtRepo.gitDirectoryURL.path stringByStandardizingPath ];
68
+ if (!gitDir) {
69
+ return nil ;
70
+ }
71
+ gitDirChangedBlock = ^(NSArray *changeEvents){
72
+ NSMutableArray *filteredEvents = [NSMutableArray new ];
73
+ for (PBGitRepositoryWatcherEventPath *event in changeEvents) {
74
+ // exclude all changes to .lock files
75
+ if ([event.path hasSuffix: @" .lock" ]) {
76
+ continue ;
77
+ }
78
+ [filteredEvents addObject: event];
79
+ }
80
+ if (filteredEvents.count ) {
81
+ [weakSelf handleGitDirEventCallback: filteredEvents];
82
+ }
83
+ };
84
+ FSEventStreamContext gitDirWatcherContext = {0 , (__bridge void *)(gitDirChangedBlock), NULL , NULL , NULL };
85
+ gitDirEventStream = FSEventStreamCreate (kCFAllocatorDefault , PBGitRepositoryWatcherCallback, &gitDirWatcherContext,
86
+ (__bridge CFArrayRef)@[gitDir],
87
+ kFSEventStreamEventIdSinceNow , 1.0 ,
88
+ kFSEventStreamCreateFlagUseCFTypes |
89
+ kFSEventStreamCreateFlagIgnoreSelf |
90
+ kFSEventStreamCreateFlagFileEvents );
91
+
67
92
}
68
- NSString *workDir = repository.gtRepo .isBare ? nil : repository.gtRepo .fileURL .path ;
69
- NSArray *paths = nil ;
70
- if (workDir) {
71
- paths = @[indexPath, workDir];
72
- } else {
73
- paths = @[indexPath];
93
+ {
94
+ workDir = repository.gtRepo .isBare ? nil : [repository.gtRepo.fileURL.path stringByStandardizingPath ];
95
+ if (workDir) {
96
+ workDirChangedBlock = ^(NSArray *changeEvents){
97
+ NSMutableArray *filteredEvents = [NSMutableArray new ];
98
+ PBGitRepositoryWatcher *watcher = weakSelf;
99
+ if (!watcher) {
100
+ return ;
101
+ }
102
+ for (PBGitRepositoryWatcherEventPath *event in changeEvents) {
103
+ // exclude anything under the .git dir
104
+ if ([event.path hasPrefix: watcher->gitDir]) {
105
+ continue ;
106
+ }
107
+ [filteredEvents addObject: event];
108
+ }
109
+ if (filteredEvents.count ) {
110
+ [watcher handleWorkDirEventCallback: filteredEvents];
111
+ }
112
+ };
113
+ FSEventStreamContext workDirWatcherContext = {0 , (__bridge void *)(workDirChangedBlock), NULL , NULL , NULL };
114
+ workDirEventStream = FSEventStreamCreate (kCFAllocatorDefault , PBGitRepositoryWatcherCallback, &workDirWatcherContext,
115
+ (__bridge CFArrayRef)@[workDir],
116
+ kFSEventStreamEventIdSinceNow , 1.0 ,
117
+ kFSEventStreamCreateFlagUseCFTypes |
118
+ kFSEventStreamCreateFlagIgnoreSelf |
119
+ kFSEventStreamCreateFlagFileEvents );
120
+ }
74
121
}
75
122
76
- self.statusCache = [NSMutableDictionary new ];
77
123
78
- // Create and activate event stream
79
- eventStream = FSEventStreamCreate (kCFAllocatorDefault , PBGitRepositoryWatcherCallback, &context,
80
- (__bridge CFArrayRef)paths,
81
- kFSEventStreamEventIdSinceNow , 1.0 ,
82
- kFSEventStreamCreateFlagUseCFTypes |
83
- kFSEventStreamCreateFlagIgnoreSelf |
84
- kFSEventStreamCreateFlagFileEvents );
124
+ self.statusCache = [NSMutableDictionary new ];
125
+
85
126
if ([PBGitDefaults useRepositoryWatcher ])
86
127
[self start ];
87
128
return self;
88
129
}
89
130
90
- - (NSDate *) _fileModificationDateAtPath : (NSString *)path {
131
+ - (NSDate *) fileModificationDateAtPath : (NSString *)path {
91
132
NSError * error;
92
133
NSDictionary *attrs = [[NSFileManager defaultManager ] attributesOfItemAtPath: path
93
134
error: &error];
@@ -99,11 +140,12 @@ - (NSDate *) _fileModificationDateAtPath:(NSString *)path {
99
140
return [attrs objectForKey: NSFileModificationDate ];
100
141
}
101
142
102
- - (BOOL ) _indexChanged {
143
+ - (BOOL ) indexChanged {
103
144
if (self.repository .isBareRepository ) {
104
145
return NO ;
105
146
}
106
- NSDate *newTouchDate = [self _fileModificationDateAtPath: [repository.gitURL.path stringByAppendingPathComponent: @" index" ]];
147
+
148
+ NSDate *newTouchDate = [self fileModificationDateAtPath: [gitDir stringByAppendingPathComponent: @" index" ]];
107
149
if (![newTouchDate isEqual: indexTouchDate]) {
108
150
indexTouchDate = newTouchDate;
109
151
return YES ;
@@ -112,7 +154,7 @@ - (BOOL) _indexChanged {
112
154
return NO ;
113
155
}
114
156
115
- - (BOOL ) _gitDirectoryChanged {
157
+ - (BOOL ) gitDirectoryChanged {
116
158
117
159
for (NSURL * fileURL in [[NSFileManager defaultManager ] contentsOfDirectoryAtURL: repository.gitURL
118
160
includingPropertiesForKeys: [NSArray arrayWithObject: NSURLContentModificationDateKey ]
@@ -140,79 +182,88 @@ - (BOOL) _gitDirectoryChanged {
140
182
return NO ;
141
183
}
142
184
143
- - (void ) _handleEventCallback : (NSArray *)eventPaths {
185
+ - (void ) handleGitDirEventCallback : (NSArray *)eventPaths
186
+ {
144
187
PBGitRepositoryWatcherEventType event = 0x0 ;
145
188
146
- if ([self _indexChanged ])
147
- {
148
- // NSLog(@"Watcher found an index change");
189
+ if ([self indexChanged ]) {
149
190
event |= PBGitRepositoryWatcherEventTypeIndex;
150
191
}
151
-
152
- NSString * ourRepo_ns = repository.gitURL .path ;
153
- // libgit2 API results for directories end with a '/'
154
- if (![ourRepo_ns hasSuffix: @" /" ])
155
- ourRepo_ns = [NSString stringWithFormat: @" %@ /" , ourRepo_ns];
156
-
192
+
193
+
157
194
NSMutableArray *paths = [NSMutableArray array ];
158
-
159
195
for (PBGitRepositoryWatcherEventPath *eventPath in eventPaths) {
160
196
// .git dir
161
- if ([[ eventPath.path stringByStandardizingPath ] isEqual: [repository.gitURL.path stringByStandardizingPath ] ]) {
162
- if ([self _gitDirectoryChanged ] || eventPath.flag != kFSEventStreamEventFlagNone ) {
197
+ if ([eventPath.path isEqualToString: gitDir ]) {
198
+ if ([self gitDirectoryChanged ] || eventPath.flag != kFSEventStreamEventFlagNone ) {
163
199
event |= PBGitRepositoryWatcherEventTypeGitDirectory;
164
200
[paths addObject: eventPath.path];
165
- // NSLog(@"Watcher: git dir change in %@", eventPath.path);
166
201
}
167
202
}
168
-
203
+ // ignore objects dir ... ?
204
+ else if ([eventPath.path rangeOfString: [gitDir stringByAppendingPathComponent: @" objects" ]].location != NSNotFound ) {
205
+ continue ;
206
+ }
207
+ // index is already covered
208
+ else if ([eventPath.path rangeOfString: [gitDir stringByAppendingPathComponent: @" index" ]].location != NSNotFound ) {
209
+ continue ;
210
+ }
169
211
// subdirs of .git dir
170
- else if ([eventPath.path rangeOfString: repository.gitURL.path].location != NSNotFound ) {
171
- // ignore changes to lock files
172
- if ([eventPath.path hasSuffix: @" .lock" ])
173
- {
174
- // NSLog(@"Watcher: ignoring change to lock file: %@", eventPath.path);
175
- continue ;
176
- }
212
+ else if ([eventPath.path rangeOfString: gitDir].location != NSNotFound ) {
177
213
event |= PBGitRepositoryWatcherEventTypeGitDirectory;
178
214
[paths addObject: eventPath.path];
179
- // NSLog(@"Watcher: git dir subdir change in %@", eventPath.path);
180
215
}
216
+ }
217
+
218
+ if (event != 0x0 ){
219
+ NSDictionary *eventInfo = @{kPBGitRepositoryEventTypeUserInfoKey :@(event),
220
+ kPBGitRepositoryEventPathsUserInfoKey :paths};
181
221
182
- else {
183
- unsigned int fileStatus = 0 ;
184
- NSString *repoPrefix = self.repository .fileURL .path ;
185
- // TODO: fix exception
186
- NSString *eventRepoRelativePath = [eventPath.path substringFromIndex: (repoPrefix.length + 1 )];
187
- int gitError = git_status_file (&fileStatus, self.repository .gtRepo .git_repository , eventRepoRelativePath.UTF8String );
188
- if (gitError == GIT_OK) {
189
- if (fileStatus & GIT_STATUS_IGNORED) {
190
- // NSLog(@"ignoring change to ignored file: %@", eventPath.path);
191
- } else {
192
- NSNumber *oldStatus = self.statusCache [eventPath.path];
193
- NSNumber *newStatus = @(fileStatus);
194
- if (![oldStatus isEqualTo: newStatus]) {
195
- // NSLog(@"file changed status: %@", eventPath.path);
196
- [paths addObject: eventPath.path];
197
- event |= PBGitRepositoryWatcherEventTypeWorkingDirectory;
198
- } else if (fileStatus & GIT_STATUS_WT_MODIFIED) {
199
- // NSLog(@"modified file touched: %@", eventPath.path);
200
- event |= PBGitRepositoryWatcherEventTypeWorkingDirectory;
201
- [paths addObject: eventPath.path];
202
- }
203
- self.statusCache [eventPath.path] = newStatus;
222
+ [[NSNotificationCenter defaultCenter ] postNotificationName: PBGitRepositoryEventNotification object: repository userInfo: eventInfo];
223
+ }
224
+ }
225
+
226
+ - (void )handleWorkDirEventCallback : (NSArray *)eventPaths
227
+ {
228
+ PBGitRepositoryWatcherEventType event = 0x0 ;
229
+
230
+ NSMutableArray *paths = [NSMutableArray array ];
231
+ for (PBGitRepositoryWatcherEventPath *eventPath in eventPaths) {
232
+ unsigned int fileStatus = 0 ;
233
+ if (![eventPath.path hasPrefix: workDir]) {
234
+ continue ;
235
+ }
236
+ if ([eventPath.path isEqualToString: workDir]) {
237
+ event |= PBGitRepositoryWatcherEventTypeWorkingDirectory;
238
+ [paths addObject: eventPath.path];
239
+ continue ;
240
+ }
241
+ NSString *eventRepoRelativePath = [eventPath.path substringFromIndex: (workDir.length + 1 )];
242
+ int gitError = git_status_file (&fileStatus, self.repository .gtRepo .git_repository , eventRepoRelativePath.UTF8String );
243
+ if (gitError == GIT_OK) {
244
+ if (fileStatus & GIT_STATUS_IGNORED) {
245
+ // NSLog(@"ignoring change to ignored file: %@", eventPath.path);
246
+ } else {
247
+ NSNumber *oldStatus = self.statusCache [eventPath.path];
248
+ NSNumber *newStatus = @(fileStatus);
249
+ if (![oldStatus isEqualTo: newStatus]) {
250
+ // NSLog(@"file changed status: %@", eventPath.path);
251
+ [paths addObject: eventPath.path];
252
+ event |= PBGitRepositoryWatcherEventTypeWorkingDirectory;
253
+ } else if (fileStatus & GIT_STATUS_WT_MODIFIED) {
254
+ // NSLog(@"modified file touched: %@", eventPath.path);
255
+ event |= PBGitRepositoryWatcherEventTypeWorkingDirectory;
256
+ [paths addObject: eventPath.path];
204
257
}
258
+ self.statusCache [eventPath.path] = newStatus;
205
259
}
206
260
}
207
261
}
208
-
262
+
209
263
if (event != 0x0 ){
210
- // NSLog(@"PBGitRepositoryWatcher firing notification for repository %@ with flag %lu", repository, event);
211
- NSDictionary *eventInfo = [NSDictionary dictionaryWithObjectsAndKeys:
212
- [NSNumber numberWithUnsignedInt: event], kPBGitRepositoryEventTypeUserInfoKey ,
213
- paths, kPBGitRepositoryEventPathsUserInfoKey ,
214
- NULL ];
215
-
264
+ NSDictionary *eventInfo = @{kPBGitRepositoryEventTypeUserInfoKey :@(event),
265
+ kPBGitRepositoryEventPathsUserInfoKey :paths};
266
+
216
267
[[NSNotificationCenter defaultCenter ] postNotificationName: PBGitRepositoryEventNotification object: repository userInfo: eventInfo];
217
268
}
218
269
}
@@ -222,20 +273,30 @@ - (void) start {
222
273
return ;
223
274
224
275
// set initial state
225
- [self _gitDirectoryChanged ];
226
- [self _indexChanged ];
276
+ [self gitDirectoryChanged ];
277
+ [self indexChanged ];
227
278
ownRef = self; // The callback has no reference to us, so we need to stay alive as long as it may be called
228
- FSEventStreamScheduleWithRunLoop (eventStream, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
229
- FSEventStreamStart (eventStream);
279
+ FSEventStreamScheduleWithRunLoop (gitDirEventStream, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
280
+ FSEventStreamStart (gitDirEventStream);
281
+
282
+ if (workDirEventStream) {
283
+ FSEventStreamScheduleWithRunLoop (workDirEventStream, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
284
+ FSEventStreamStart (workDirEventStream);
285
+ }
286
+
230
287
_running = YES ;
231
288
}
232
289
233
290
- (void ) stop {
234
291
if (!_running)
235
292
return ;
236
293
237
- FSEventStreamStop (eventStream);
238
- FSEventStreamUnscheduleFromRunLoop (eventStream, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
294
+ if (workDirEventStream) {
295
+ FSEventStreamStop (workDirEventStream);
296
+ FSEventStreamUnscheduleFromRunLoop (workDirEventStream, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
297
+ }
298
+ FSEventStreamStop (gitDirEventStream);
299
+ FSEventStreamUnscheduleFromRunLoop (gitDirEventStream, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
239
300
ownRef = nil ; // Now that we can't be called anymore, we can allow ourself to be -dealloc'd
240
301
_running = NO ;
241
302
}
0 commit comments