Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 351 lines (283 sloc) 10.837 kb
65b61d4 @pieter add more files
pieter authored
1 //
2 // PBGitCommitController.m
3 // GitX
4 //
5 // Created by Pieter de Bie on 19-09-08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "PBGitCommitController.h"
252796e @pieter CommitView: Show basic files
pieter authored
10 #import "NSFileHandleExt.h"
11 #import "PBChangedFile.h"
65b61d4 @pieter add more files
pieter authored
12
13 @implementation PBGitCommitController
14
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
15 @synthesize files, status, busy, amend;
252796e @pieter CommitView: Show basic files
pieter authored
16
17 - (void)awakeFromNib
18 {
e329493 @pieter Move toolbar to view xib
pieter authored
19 [super awakeFromNib];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
20 self.busy = 0;
21
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
22 amend = NO;
23
a42adc6 @pieter CommitView: Allow doubleclick to (un)stage changes
pieter authored
24 [unstagedButtonCell setAction:@selector(rowClicked:)];
25 [cachedButtonCell setAction:@selector(rowClicked:)];
26
27 [unstagedTable setDoubleAction:@selector(tableClicked:)];
28 [cachedTable setDoubleAction:@selector(tableClicked:)];
5111b36 @ciaran Setting up target/action for the file icon buttons
ciaran authored
29
472d36c @pieter CommitView: Add context menu to revert changes
pieter authored
30 [unstagedTable setController: self];
31
44009b6 @pieter CommitView: use only one array for all files
pieter authored
32 [self refresh:self];
978d7cb @pieter CommitView: Correctly set the default commitmessage font to Monaco 12
pieter authored
33
34 [commitMessageView setTypingAttributes:[NSDictionary dictionaryWithObject:[NSFont fontWithName:@"Monaco" size:12.0] forKey:NSFontAttributeName]];
76e176e @pieter Add staging and unstaging of files
pieter authored
35
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
36 [unstagedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasUnstagedChanges == 1"]];
37 [cachedFilesController setFilterPredicate:[NSPredicate predicateWithFormat:@"hasCachedChanges == 1"]];
d58127e @pieter CommitView: Sort items based on status and then path
pieter authored
38
39 [unstagedFilesController setSortDescriptors:[NSArray arrayWithObjects:
40 [[NSSortDescriptor alloc] initWithKey:@"status" ascending:false],
41 [[NSSortDescriptor alloc] initWithKey:@"path" ascending:true], nil]];
42 [cachedFilesController setSortDescriptors:[NSArray arrayWithObject:
43 [[NSSortDescriptor alloc] initWithKey:@"path" ascending:true]]];
252796e @pieter CommitView: Show basic files
pieter authored
44 }
45
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
46 - (void) setAmend:(BOOL)newAmend
47 {
48 if (newAmend == amend)
49 return;
50 amend = newAmend;
51
52 if (amend && [[commitMessageView string] length] <= 3)
53 commitMessageView.string = [repository outputForCommand:@"log -1 --pretty=format:%s%n%n%b HEAD"];
54
55 [self refresh:self];
56 }
57
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
58 - (NSArray *) linesFromNotification:(NSNotification *)notification
252796e @pieter CommitView: Show basic files
pieter authored
59 {
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
60 NSDictionary *userInfo = [notification userInfo];
61 NSData *data = [userInfo valueForKey:NSFileHandleNotificationDataItem];
62 if (!data)
63 return NULL;
64
65 NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
66 if (!string)
67 return NULL;
252796e @pieter CommitView: Show basic files
pieter authored
68
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
69 // Strip trailing newline
70 if ([string hasSuffix:@"\n"])
71 string = [string substringToIndex:[string length]-1];
72
7a5716b @pieter CommitView: Use null-terminated strings when reading the index
pieter authored
73 NSArray *lines = [string componentsSeparatedByString:@"\0"];
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
74 return lines;
75 }
76
d66a105 @pieter CommitView: Allow rootless commits
pieter authored
77 - (NSString *) parentTree
78 {
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
79 NSString *parent = amend ? @"HEAD^" : @"HEAD";
80
41a906d @pieter PBGitRepository: Don't return a ref if we error out
pieter authored
81 if (![repository parseReference:parent])
d66a105 @pieter CommitView: Allow rootless commits
pieter authored
82 // We don't have a head ref. Return the empty tree.
83 return @"4b825dc642cb6eb9a060e54bf8d69288fbee4904";
84
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
85 return parent;
d66a105 @pieter CommitView: Allow rootless commits
pieter authored
86 }
87
44009b6 @pieter CommitView: use only one array for all files
pieter authored
88 - (void) refresh:(id) sender
89 {
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
90 self.status = @"Refreshing index…";
91 self.busy++;
b005066 @pieter CommitView: Make sure controllers are notified on refresh
pieter authored
92 self.files = [NSMutableArray array];
93
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
94 [repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"update-index", @"-q", @"--unmerged", @"--ignore-missing", @"--refresh", nil]];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
95 self.busy--;
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
96
97 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
98 [nc removeObserver:self];
99
100 // Other files
8c59a12 @pieter CommitView: Also read in new objects with null-separator
pieter authored
101 NSArray *arguments = [NSArray arrayWithObjects:@"ls-files", @"--others", @"--exclude-standard", @"-z", nil];
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
102 NSFileHandle *handle = [repository handleInWorkDirForArguments:arguments];
4131258 @pieter CommitController: Read index data to EOF
pieter authored
103 [nc addObserver:self selector:@selector(readOtherFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
104 self.busy++;
4131258 @pieter CommitController: Read index data to EOF
pieter authored
105 [handle readToEndOfFileInBackgroundAndNotify];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
106
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
107 // Unstaged files
7a5716b @pieter CommitView: Use null-terminated strings when reading the index
pieter authored
108 handle = [repository handleInWorkDirForArguments:[NSArray arrayWithObjects:@"diff-files", @"-z", nil]];
4131258 @pieter CommitController: Read index data to EOF
pieter authored
109 [nc addObserver:self selector:@selector(readUnstagedFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
110 self.busy++;
4131258 @pieter CommitController: Read index data to EOF
pieter authored
111 [handle readToEndOfFileInBackgroundAndNotify];
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
112
113 // Cached files
d66a105 @pieter CommitView: Allow rootless commits
pieter authored
114 handle = [repository handleInWorkDirForArguments:[NSArray arrayWithObjects:@"diff-index", @"--cached", @"-z", [self parentTree], nil]];
4131258 @pieter CommitController: Read index data to EOF
pieter authored
115 [nc addObserver:self selector:@selector(readCachedFiles:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
116 self.busy++;
4131258 @pieter CommitController: Read index data to EOF
pieter authored
117 [handle readToEndOfFileInBackgroundAndNotify];
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
118
44009b6 @pieter CommitView: use only one array for all files
pieter authored
119 self.files = files;
252796e @pieter CommitView: Show basic files
pieter authored
120 }
121
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
122 - (void) doneProcessingIndex
123 {
124 if (!--self.busy)
125 self.status = @"Ready";
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
126 [unstagedFilesController didChangeArrangementCriteria];
127 [cachedFilesController didChangeArrangementCriteria];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
128 }
129
130 - (void) readOtherFiles:(NSNotification *)notification;
131 {
132 NSArray *lines = [self linesFromNotification:notification];
133 for (NSString *line in lines) {
134 if ([line length] == 0)
135 continue;
136 PBChangedFile *file =[[PBChangedFile alloc] initWithPath:line andRepository:repository];
137 file.status = NEW;
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
138 file.hasCachedChanges = NO;
139 file.hasUnstagedChanges = YES;
140
141 [files addObject: file];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
142 }
143 [self doneProcessingIndex];
144 }
145
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
146 - (void) addFilesFromLines:(NSArray *)lines cached:(BOOL) cached
252796e @pieter CommitView: Show basic files
pieter authored
147 {
7a5716b @pieter CommitView: Use null-terminated strings when reading the index
pieter authored
148 NSArray *fileStatus;
149 int even = 0;
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
150 for (NSString *line in lines) {
7a5716b @pieter CommitView: Use null-terminated strings when reading the index
pieter authored
151 if (!even) {
152 even = 1;
153 fileStatus = [line componentsSeparatedByString:@" "];
154 continue;
155 }
156 even = 0;
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
157
472d36c @pieter CommitView: Add context menu to revert changes
pieter authored
158 BOOL isNew = YES;
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
159 // If the file is already added, we shouldn't add it again
160 // but rather update it to incorporate our changes
472d36c @pieter CommitView: Add context menu to revert changes
pieter authored
161 for (PBChangedFile *file in files) {
162 if ([file.path isEqualToString:line]) {
163 if (cached)
164 file.hasCachedChanges = YES;
165 else
166 file.hasUnstagedChanges = YES;
167 isNew = NO;
168 break;
169 }
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
170 }
171
472d36c @pieter CommitView: Add context menu to revert changes
pieter authored
172 if (!isNew)
173 continue;
174
7a5716b @pieter CommitView: Use null-terminated strings when reading the index
pieter authored
175 PBChangedFile *file = [[PBChangedFile alloc] initWithPath:line andRepository:repository];
a0f248e @pieter CommitView: Also show deleted files correctly
pieter authored
176 if ([[fileStatus objectAtIndex:4] isEqualToString:@"D"])
177 file.status = DELETED;
178 else
179 file.status = MODIFIED;
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
180
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
181 file.hasCachedChanges = cached;
182 file.hasUnstagedChanges = !cached;
b243e86 @pieter CommitController: Don't show files that are in conflict twice
pieter authored
183
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
184 [files addObject: file];
252796e @pieter CommitView: Show basic files
pieter authored
185 }
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
186
187 }
188
189 - (void) readUnstagedFiles:(NSNotification *)notification
190 {
191 NSArray *lines = [self linesFromNotification:notification];
192 [self addFilesFromLines:lines cached:NO];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
193 [self doneProcessingIndex];
252796e @pieter CommitView: Show basic files
pieter authored
194 }
195
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
196 - (void) readCachedFiles:(NSNotification *)notification
252796e @pieter CommitView: Show basic files
pieter authored
197 {
55b37d9 @pieter CommitView: Asynchronously read in the files
pieter authored
198 NSArray *lines = [self linesFromNotification:notification];
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
199 [self addFilesFromLines:lines cached:YES];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
200 [self doneProcessingIndex];
201 }
202
203 - (void) commitFailedBecause:(NSString *)reason
204 {
205 self.busy--;
206 self.status = [@"Commit failed: " stringByAppendingString:reason];
207 [[NSAlert alertWithMessageText:@"Commit failed"
208 defaultButton:nil
209 alternateButton:nil
210 otherButton:nil
211 informativeTextWithFormat:reason] runModal];
212 return;
252796e @pieter CommitView: Show basic files
pieter authored
213 }
214
3f923f5 @pieter CommitView: Add commit capability
pieter authored
215 - (IBAction) commit:(id) sender
216 {
b753ab0 @pieter CommitView: Don't allow empty commits
pieter authored
217 if ([[cachedFilesController arrangedObjects] count] == 0) {
218 [[NSAlert alertWithMessageText:@"No changes to commit"
219 defaultButton:nil
220 alternateButton:nil
221 otherButton:nil
222 informativeTextWithFormat:@"You must first stage some changes before committing"] runModal];
223 return;
224 }
225
3f923f5 @pieter CommitView: Add commit capability
pieter authored
226 NSString *commitMessage = [commitMessageView string];
227 if ([commitMessage length] < 3) {
228 [[NSAlert alertWithMessageText:@"Commitmessage missing"
229 defaultButton:nil
230 alternateButton:nil
231 otherButton:nil
232 informativeTextWithFormat:@"Please enter a commit message before committing"] runModal];
233 return;
234 }
fd146bd @pieter CommitView: Add a better reflog entry
pieter authored
235
69e1c6f @pieter CommitView: Don't break on oneliners
pieter authored
236 NSString *commitSubject;
237 NSRange newLine = [commitMessage rangeOfString:@"\n"];
238 if (newLine.location == NSNotFound)
fd146bd @pieter CommitView: Add a better reflog entry
pieter authored
239 commitSubject = commitMessage;
69e1c6f @pieter CommitView: Don't break on oneliners
pieter authored
240 else
241 commitSubject = [commitMessage substringToIndex:newLine.location];
b753ab0 @pieter CommitView: Don't allow empty commits
pieter authored
242
fd146bd @pieter CommitView: Add a better reflog entry
pieter authored
243 commitSubject = [@"commit: " stringByAppendingString:commitSubject];
244
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
245 self.busy++;
246 self.status = @"Creating tree..";
3f923f5 @pieter CommitView: Add commit capability
pieter authored
247 NSString *tree = [repository outputForCommand:@"write-tree"];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
248 if ([tree length] != 40)
249 return [self commitFailedBecause:@"Could not create a tree"];
250
3f923f5 @pieter CommitView: Add commit capability
pieter authored
251 int ret;
d66a105 @pieter CommitView: Allow rootless commits
pieter authored
252
253 NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"commit-tree", tree, nil];
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
254 NSString *parent = amend ? @"HEAD^" : @"HEAD";
255 if ([repository parseReference:parent]) {
d66a105 @pieter CommitView: Allow rootless commits
pieter authored
256 [arguments addObject:@"-p"];
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
257 [arguments addObject:parent];
d66a105 @pieter CommitView: Allow rootless commits
pieter authored
258 }
259
260 NSString *commit = [repository outputForArguments:arguments
3f923f5 @pieter CommitView: Add commit capability
pieter authored
261 inputString:commitMessage
262 retValue: &ret];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
263
264 if (ret || [commit length] != 40)
265 return [self commitFailedBecause:@"Could not create a commit object"];
3f923f5 @pieter CommitView: Add commit capability
pieter authored
266
fd146bd @pieter CommitView: Add a better reflog entry
pieter authored
267 [repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-m", commitSubject, @"HEAD", commit, nil]
3f923f5 @pieter CommitView: Add commit capability
pieter authored
268 retValue: &ret];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
269 if (ret)
270 return [self commitFailedBecause:@"Could not update HEAD"];
271
90db001 @pieter WebCommitView: Add a state display
pieter authored
272 [webController setStateMessage:[NSString stringWithFormat:@"Succesfully created commit %@", commit]];
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
273
a2217fe @pieter Reload commits after doing a commit
pieter authored
274 repository.hasChanged = YES;
0ad81bc @pieter CommitView: Use the status bar to update on status
pieter authored
275 self.busy--;
2676a20 @pieter Fix some compiler warnings
pieter authored
276 [commitMessageView setString:@""];
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
277 amend = NO;
2676a20 @pieter Fix some compiler warnings
pieter authored
278 [self refresh:self];
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
279 self.amend = NO;
3f923f5 @pieter CommitView: Add commit capability
pieter authored
280 }
281
a42adc6 @pieter CommitView: Allow doubleclick to (un)stage changes
pieter authored
282 - (void) tableClicked:(NSTableView *) tableView
44009b6 @pieter CommitView: use only one array for all files
pieter authored
283 {
37cda5f @pieter CommitView: Intelligently add files
pieter authored
284 NSUInteger selectionIndex = [[tableView selectedRowIndexes] firstIndex];
472d36c @pieter CommitView: Add context menu to revert changes
pieter authored
285 NSArrayController *controller = [tableView tag] == 0 ? unstagedFilesController : cachedFilesController;
37cda5f @pieter CommitView: Intelligently add files
pieter authored
286 PBChangedFile *selectedItem = [[controller arrangedObjects] objectAtIndex:selectionIndex];
472d36c @pieter CommitView: Add context menu to revert changes
pieter authored
287
5010511 @pieter CommitView: Always use a single PBChangedFile object
pieter authored
288 if ([tableView tag] == 0)
37cda5f @pieter CommitView: Intelligently add files
pieter authored
289 [selectedItem stageChanges];
290 else
e659e63 @pieter CommitView: Add option to amend commits
pieter authored
291 [selectedItem unstageChangesAmend:amend];
37cda5f @pieter CommitView: Intelligently add files
pieter authored
292
44009b6 @pieter CommitView: use only one array for all files
pieter authored
293 }
252796e @pieter CommitView: Show basic files
pieter authored
294
a42adc6 @pieter CommitView: Allow doubleclick to (un)stage changes
pieter authored
295 - (void) rowClicked:(NSCell *)sender
296 {
297 NSTableView *tableView = (NSTableView *)[sender controlView];
298 if([tableView numberOfSelectedRows] != 1)
299 return;
300 [self tableClicked: tableView];
301 }
302
7be97dc @ciaran Using button cells instead of image cells, for the icons, so we can h…
ciaran authored
303 - (void)tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(int)rowIndex
304 {
b8292f1 @ciaran Adding an icon and text cell implementation from CocoaDev
ciaran authored
305 [[tableColumn dataCell] setImage:[[[(([tableView tag] == 0) ? unstagedFilesController : cachedFilesController) arrangedObjects] objectAtIndex:rowIndex] icon]];
7be97dc @ciaran Using button cells instead of image cells, for the icons, so we can h…
ciaran authored
306 }
472d36c @pieter CommitView: Add context menu to revert changes
pieter authored
307
308 - (NSMenu *) menuForTable:(NSTableView *)table
309 {
310 NSUInteger selectionIndex = [[table selectedRowIndexes] firstIndex];
311 PBChangedFile *selectedItem = [[unstagedFilesController arrangedObjects] objectAtIndex:selectionIndex];
312
313 NSMenu *a = [[NSMenu alloc] init];
314 NSMenuItem *stageItem = [[NSMenuItem alloc] initWithTitle:@"Stage Changes" action:@selector(stageChanges) keyEquivalent:@""];
315 [stageItem setTarget:selectedItem];
316 [a addItem:stageItem];
317
318 // Do not add "revert" options for untracked files
319 if (selectedItem.status == NEW)
320 return a;
321
322 NSMenuItem *revertItem = [[NSMenuItem alloc] initWithTitle:@"Revert Changes…" action:@selector(revertChanges) keyEquivalent:@""];
323 [revertItem setTarget:selectedItem];
324 [revertItem setAlternate:NO];
325 [a addItem:revertItem];
326
327 NSMenuItem *revertForceItem = [[NSMenuItem alloc] initWithTitle:@"Revert Changes" action:@selector(forceRevertChanges) keyEquivalent:@""];
328 [revertForceItem setTarget:selectedItem];
329 [revertForceItem setAlternate:YES];
330 [revertForceItem setKeyEquivalentModifierMask:NSAlternateKeyMask];
331 [a addItem:revertForceItem];
332
333 return a;
334 }
b252de1 @pieter CommitView: Allow committing per hunk
pieter authored
335
336 - (void) stageHunk:(NSString *)hunk reverse:(BOOL)reverse
337 {
338 NSMutableArray *array = [NSMutableArray arrayWithObjects:@"apply", @"--cached", nil];
339 if (reverse)
340 [array addObject:@"--reverse"];
341
342 int ret;
343 NSString *error = [repository outputForArguments:array
344 inputString:hunk
345 retValue: &ret];
346 if (ret)
347 NSLog(@"Error: %@", error);
348 [self refresh:self]; // TODO: We should do this smarter by checking if the file diff is empty, which is faster.
349 }
65b61d4 @pieter add more files
pieter authored
350 @end
Something went wrong with that request. Please try again.