11
11
#import " PBChangedFile.h"
12
12
#import " PBWebChangesController.h"
13
13
14
+
15
+ @interface PBGitCommitController (PrivateMethods)
16
+ - (NSArray *) linesFromNotification : (NSNotification *)notification ;
17
+ - (void ) doneProcessingIndex ;
18
+ - (NSMutableDictionary *)dictionaryForLines : (NSArray *)lines ;
19
+ - (void ) addFilesFromDictionary : (NSMutableDictionary *)dictionary staged : (BOOL )staged tracked : (BOOL )tracked ;
20
+ @end
21
+
14
22
@implementation PBGitCommitController
15
23
16
24
@synthesize files, status, busy, amend;
@@ -24,7 +32,7 @@ - (void)awakeFromNib
24
32
[commitMessageView setTypingAttributes: [NSDictionary dictionaryWithObject: [NSFont fontWithName: @" Monaco" size: 12.0 ] forKey: NSFontAttributeName ]];
25
33
26
34
[unstagedFilesController setFilterPredicate: [NSPredicate predicateWithFormat: @" hasUnstagedChanges == 1" ]];
27
- [cachedFilesController setFilterPredicate: [NSPredicate predicateWithFormat: @" hasCachedChanges == 1" ]];
35
+ [cachedFilesController setFilterPredicate: [NSPredicate predicateWithFormat: @" hasStagedChanges == 1" ]];
28
36
29
37
[unstagedFilesController setSortDescriptors: [NSArray arrayWithObjects:
30
38
[[NSSortDescriptor alloc ] initWithKey: @" status" ascending: false ],
@@ -91,24 +99,22 @@ - (NSString *) parentTree
91
99
92
100
- (void ) refresh : (id ) sender
93
101
{
94
- for (PBChangedFile *file in files )
95
- file. shouldBeDeleted = YES ;
102
+ if (![repository workingDirectory ] )
103
+ return ;
96
104
97
105
self.status = @" Refreshing index…" ;
98
- if (![repository workingDirectory ]) {
99
- // if ([[repository outputForCommand:@"rev-parse --is-inside-work-tree"] isEqualToString:@"false"]) {
100
- return ;
101
- }
102
106
103
- self.busy ++;
107
+ // If self.busy reaches 0, all tasks have finished
108
+ self.busy = 0 ;
104
109
110
+ // Refresh the index, necessary for the next methods (that's why it's blocking)
111
+ // FIXME: Make this non-blocking. This call can be expensive in large repositories
105
112
[repository outputInWorkdirForArguments: [NSArray arrayWithObjects: @" update-index" , @" -q" , @" --unmerged" , @" --ignore-missing" , @" --refresh" , nil ]];
106
- self.busy --;
107
113
108
114
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter ];
109
115
[nc removeObserver: self ];
110
116
111
- // Other files
117
+ // Other files (not tracked, not ignored)
112
118
NSArray *arguments = [NSArray arrayWithObjects: @" ls-files" , @" --others" , @" --exclude-standard" , @" -z" , nil ];
113
119
NSFileHandle *handle = [repository handleInWorkDirForArguments: arguments];
114
120
[nc addObserver: self selector: @selector (readOtherFiles: ) name: NSFileHandleReadToEndOfFileCompletionNotification object: handle];
@@ -121,7 +127,7 @@ - (void) refresh:(id) sender
121
127
self.busy ++;
122
128
[handle readToEndOfFileInBackgroundAndNotify ];
123
129
124
- // Cached files
130
+ // Staged files
125
131
handle = [repository handleInWorkDirForArguments: [NSArray arrayWithObjects: @" diff-index" , @" --cached" , @" -z" , [self parentTree ], nil ]];
126
132
[nc addObserver: self selector: @selector (readCachedFiles: ) name: NSFileHandleReadToEndOfFileCompletionNotification object: handle];
127
133
self.busy ++;
@@ -133,15 +139,19 @@ - (void) updateView
133
139
[self refresh: nil ];
134
140
}
135
141
142
+ // This method is called for each of the three processes from above.
143
+ // If all three are finished (self.busy == 0), then we can delete
144
+ // all files previously marked as deletable
136
145
- (void ) doneProcessingIndex
137
146
{
138
147
[self willChangeValueForKey: @" files" ];
139
148
if (!--self.busy ) {
140
149
self.status = @" Ready" ;
141
- NSArray *filesToBeDeleted = [ files filteredArrayUsingPredicate: [ NSPredicate predicateWithFormat: @" shouldBeDeleted == 1 " ]];
142
- for (PBChangedFile * file in filesToBeDeleted ) {
150
+ for (PBChangedFile *file in files) {
151
+ if (! file. hasStagedChanges && !file. hasUnstagedChanges ) {
143
152
NSLog (@" Deleting file: %@ " , [file path ]);
144
153
[files removeObject: file];
154
+ }
145
155
}
146
156
}
147
157
[self didChangeValueForKey: @" files" ];
@@ -151,105 +161,109 @@ - (void) readOtherFiles:(NSNotification *)notification;
151
161
{
152
162
[unstagedFilesController setAutomaticallyRearrangesObjects: NO ];
153
163
NSArray *lines = [self linesFromNotification: notification];
154
- for (NSString *line in lines) {
155
- if ([line length ] == 0 )
164
+ NSMutableDictionary *dictionary = [[NSMutableDictionary alloc ] initWithCapacity: [lines count ]];
165
+ // We fake this files status as good as possible.
166
+ NSArray *fileStatus = [NSArray arrayWithObjects: @" :000000" , @" 100644" , @" 0000000000000000000000000000000000000000" , @" 0000000000000000000000000000000000000000" , @" A" , nil ];
167
+ for (NSString *path in lines) {
168
+ if ([path length ] == 0 )
156
169
continue ;
157
-
158
- BOOL added = NO ;
159
- // Check if file is already in our index
160
- for (PBChangedFile *file in files) {
161
- if ([[file path ] isEqualToString: line]) {
162
- file.shouldBeDeleted = NO ;
163
- added = YES ;
164
- file.status = NEW;
165
- file.hasCachedChanges = NO ;
166
- file.hasUnstagedChanges = YES ;
167
- break ;
168
- }
169
- }
170
-
171
- if (added)
172
- continue ;
173
-
174
- // File does not exist yet, so add it
175
- PBChangedFile *file =[[PBChangedFile alloc ] initWithPath: line];
176
- file.status = NEW;
177
- file.hasCachedChanges = NO ;
178
- file.hasUnstagedChanges = YES ;
179
- [files addObject: file];
170
+ [dictionary setObject: fileStatus forKey: path];
180
171
}
181
- [unstagedFilesController setAutomaticallyRearrangesObjects: YES ];
182
- [unstagedFilesController rearrangeObjects ];
172
+ [self addFilesFromDictionary: dictionary staged: NO tracked: NO ];
183
173
[self doneProcessingIndex ];
184
174
}
185
175
186
- - (void ) addFilesFromLines : (NSArray *)lines cached : ( BOOL ) cached
176
+ - (NSMutableDictionary *) dictionaryForLines : (NSArray *)lines
187
177
{
178
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity: [lines count ]/2 ];
179
+
180
+ // Fill the dictionary with the new information
188
181
NSArray *fileStatus;
189
- int even = 0 ;
182
+ BOOL even = FALSE ;
190
183
for (NSString *line in lines) {
191
184
if (!even) {
192
- even = 1 ;
185
+ even = TRUE ;
193
186
fileStatus = [line componentsSeparatedByString: @" " ];
194
187
continue ;
195
188
}
196
- even = 0 ;
197
189
198
- NSString *mode = [[fileStatus objectAtIndex: 0 ] substringFromIndex: 1 ];
199
- NSString *sha = [fileStatus objectAtIndex: 2 ];
190
+ even = FALSE ;
191
+ [dictionary setObject: fileStatus forKey: line];
192
+ }
193
+ return dictionary;
194
+ }
200
195
201
- BOOL isNew = YES ;
202
- // If the file is already added, we shouldn't add it again
203
- // but rather update it to incorporate our changes
204
- for (PBChangedFile *file in files) {
205
- if ([file.path isEqualToString: line]) {
206
- if (cached && file.hasCachedChanges ) { // if we're listing cached files
207
- file.shouldBeDeleted = NO ; // and the matching file in files had cached changes
208
- file.commitBlobSHA = sha; // we don't delete it
196
+ - (void ) addFilesFromDictionary : (NSMutableDictionary *)dictionary staged : (BOOL )staged tracked : (BOOL )tracked
197
+ {
198
+ // Iterate over all existing files
199
+ for (PBChangedFile *file in files) {
200
+ NSArray *fileStatus = [dictionary objectForKey: file.path];
201
+ // Object found, this is still a cached / uncached thing
202
+ if (fileStatus) {
203
+ if (tracked) {
204
+ NSString *mode = [[fileStatus objectAtIndex: 0 ] substringFromIndex: 1 ];
205
+ NSString *sha = [fileStatus objectAtIndex: 2 ];
206
+
207
+ if (staged) {
208
+ file.hasStagedChanges = YES ;
209
+ file.commitBlobSHA = sha;
209
210
file.commitBlobMode = mode;
210
- isNew = NO ;
211
- break ;
212
- } else if ((!cached) && file. hasUnstagedChanges ) { // if we're listing unstaged files and the
213
- file. shouldBeDeleted = NO ; // matching file in files had unstaged changes
214
- isNew = NO ; // we don't delete it
215
- break ;
216
- }
211
+ } else
212
+ file. hasUnstagedChanges = YES ;
213
+ } else {
214
+ // Untracked file, set status to NEW, only unstaged changes
215
+ file. hasStagedChanges = NO ;
216
+ file. hasUnstagedChanges = YES ;
217
+ file. status = NEW;
217
218
}
219
+ [dictionary removeObjectForKey: file.path];
220
+ } else { // Object not found, let's remove it from the changes
221
+ if (staged)
222
+ file.hasStagedChanges = NO ;
223
+ else if (tracked && file.status != NEW) // Only remove it if it's not an untracked file. We handle that with the other thing
224
+ file.hasUnstagedChanges = NO ;
225
+ else if (!tracked && file.status == NEW)
226
+ file.hasUnstagedChanges = NO ;
218
227
}
228
+ }
219
229
220
- if (!isNew)
221
- continue ;
230
+ // Do new files
231
+ for (NSString *path in [dictionary allKeys ]) {
232
+ NSArray *fileStatus = [dictionary objectForKey: path];
222
233
223
- PBChangedFile *file = [[PBChangedFile alloc ] initWithPath: line ];
234
+ PBChangedFile *file = [[PBChangedFile alloc ] initWithPath: path ];
224
235
if ([[fileStatus objectAtIndex: 4 ] isEqualToString: @" D" ])
225
236
file.status = DELETED;
226
237
else if ([[fileStatus objectAtIndex: 0 ] isEqualToString: @" :000000" ])
227
238
file.status = NEW;
228
239
else
229
240
file.status = MODIFIED;
230
241
231
- file.commitBlobSHA = sha;
232
- file.commitBlobMode = mode;
242
+ if (staged) {
243
+ file.commitBlobSHA = [[fileStatus objectAtIndex: 0 ] substringFromIndex: 1 ];
244
+ file.commitBlobMode = [fileStatus objectAtIndex: 2 ];
245
+ }
233
246
234
- file.hasCachedChanges = cached ;
235
- file.hasUnstagedChanges = !cached ;
247
+ file.hasStagedChanges = staged ;
248
+ file.hasUnstagedChanges = !staged ;
236
249
237
250
[files addObject: file];
238
251
}
239
-
240
252
}
241
253
242
254
- (void ) readUnstagedFiles : (NSNotification *)notification
243
255
{
244
256
NSArray *lines = [self linesFromNotification: notification];
245
- [self addFilesFromLines: lines cached: NO ];
257
+ NSMutableDictionary *dic = [self dictionaryForLines: lines];
258
+ [self addFilesFromDictionary: dic staged: NO tracked: YES ];
246
259
[self doneProcessingIndex ];
247
260
}
248
261
249
262
- (void ) readCachedFiles : (NSNotification *)notification
250
263
{
251
264
NSArray *lines = [self linesFromNotification: notification];
252
- [self addFilesFromLines: lines cached: YES ];
265
+ NSMutableDictionary *dic = [self dictionaryForLines: lines];
266
+ [self addFilesFromDictionary: dic staged: YES tracked: YES ];
253
267
[self doneProcessingIndex ];
254
268
}
255
269
@@ -356,12 +370,16 @@ - (void) stageHunk:(NSString *)hunk reverse:(BOOL)reverse
356
370
if (reverse)
357
371
[array addObject: @" --reverse" ];
358
372
359
- int ret;
373
+ int ret = 1 ;
360
374
NSString *error = [repository outputForArguments: array
361
375
inputString: hunk
362
- retValue: &ret];
376
+ retValue: &ret];
377
+
378
+ // FIXME: show this error, rather than just logging it
363
379
if (ret)
364
380
NSLog (@" Error: %@ " , error);
365
- [self refresh: self ]; // TODO: We should do this smarter by checking if the file diff is empty, which is faster.
381
+
382
+ // TODO: We should do this smarter by checking if the file diff is empty, which is faster.
383
+ [self refresh: self ];
366
384
}
367
385
@end
0 commit comments