CommitView: Don't keep rearranging when iterating over files
[GitX.git] / PBGitTree.m
blobc8003d1a043e697a2f65202c931b611b0013caf2
1 //
2 //  PBGitTree.m
3 //  GitTest
4 //
5 //  Created by Pieter de Bie on 15-06-08.
6 //  Copyright 2008 __MyCompanyName__. All rights reserved.
7 //
9 #import "PBGitTree.h"
10 #import "PBGitCommit.h"
11 #import "NSFileHandleExt.h"
12 #import "PBEasyPipe.h"
13 #import "PBEasyFS.h"
15 @implementation PBGitTree
17 @synthesize sha, path, repository, leaf, parent;
19 + (PBGitTree*) rootForCommit:(id) commit
21         PBGitCommit* c = commit;
22         PBGitTree* tree = [[self alloc] init];
23         tree.parent = nil;
24         tree.leaf = NO;
25         tree.sha = [c realSha];
26         tree.repository = c.repository;
27         tree.path = @"";
28         return tree;
31 + (PBGitTree*) treeForTree: (PBGitTree*) prev andPath: (NSString*) path;
33         PBGitTree* tree = [[self alloc] init];
34         tree.parent = prev;
35         tree.sha = prev.sha;
36         tree.repository = prev.repository;
37         tree.path = path;
38         return tree;
41 - init
43         children = nil;
44         localFileName = nil;
45         leaf = YES;
46         return self;
49 - (NSString*) refSpec
51         return [NSString stringWithFormat:@"%@:%@", self.sha, self.fullPath];
54 - (BOOL) isLocallyCached
56         NSFileManager* fs = [NSFileManager defaultManager];
57         if (localFileName && [fs fileExistsAtPath:localFileName])
58         {
59                 NSDate* mtime = [[fs attributesOfItemAtPath:localFileName error: nil] objectForKey:NSFileModificationDate];
60                 if ([mtime compare:localMtime] == 0)
61                         return YES;
62         }
63         return NO;
66 - (BOOL)hasBinaryHeader:(NSString*)contents
68         if(!contents)
69                 return NO;
71         return [contents rangeOfString:@"\0" options:0 range:NSMakeRange(0, ([contents length] >= 8000) ? 7999 : [contents length])].location != NSNotFound;
74 - (BOOL)hasBinaryAttributes
76         // First ask git check-attr if the file has a binary attribute custom set
77         NSFileHandle *handle = [repository handleInWorkDirForArguments:[NSArray arrayWithObjects:@"check-attr", @"binary", [self fullPath], nil]];
78         NSData *data = [handle readDataToEndOfFile];
79         NSString *string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
81         if (!string)
82                 return NO;
83         string = [string stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
85         if ([string hasSuffix:@"binary: set"])
86                 return YES;
88         if ([string hasSuffix:@"binary: unset"])
89                 return NO;
91         // Binary state unknown, do a check on common filename-extensions
92         for (NSString *extension in [NSArray arrayWithObjects:@".pdf", @".jpg", @".jpeg", @".png", @".bmp", @".gif", @".o", nil]) {
93                 if ([[self fullPath] hasSuffix:extension])
94                         return YES;
95         }
97         return NO;
100 - (NSString*) contents
102         if (!leaf)
103                 return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]];
105         if ([self isLocallyCached]) {
106                 NSData *data = [NSData dataWithContentsOfFile:localFileName];
107                 NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
108                 if (!string)
109                         string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
110                 return string;
111         }
112         
113         return [repository outputForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]];
116 - (long long)fileSize
118         if (_fileSize)
119                 return _fileSize;
121         NSFileHandle *handle = [repository handleForArguments:[NSArray arrayWithObjects:@"cat-file", @"-s", [self refSpec], nil]];
122         NSString *sizeString = [[NSString alloc] initWithData:[handle readDataToEndOfFile] encoding:NSISOLatin1StringEncoding];
124         if (!sizeString)
125                 _fileSize = -1;
126         else
127                 _fileSize = [sizeString longLongValue];
129         return _fileSize;
132 - (NSString *)textContents
134         if (!leaf)
135                 return [NSString stringWithFormat:@"This is a tree with path %@", [self fullPath]];
137         if ([self hasBinaryAttributes])
138                 return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]];
140         if ([self fileSize] > 52428800) // ~50MB
141                 return [NSString stringWithFormat:@"%@ is too big to be displayed (%d bytes)", [self fullPath], [self fileSize]];
143         NSString* contents = [self contents];
145         if ([self hasBinaryHeader:contents])
146                 return [NSString stringWithFormat:@"%@ appears to be a binary file of %d bytes", [self fullPath], [self fileSize]];
148         return contents;
151 - (void) saveToFolder: (NSString *) dir
153         NSString* newName = [dir stringByAppendingPathComponent:path];
155         if (leaf) {
156                 NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]];
157                 NSData* data = [handle readDataToEndOfFile];
158                 [data writeToFile:newName atomically:YES];
159         } else { // Directory
160                 [[NSFileManager defaultManager] createDirectoryAtPath:newName attributes:nil];
161                 for (PBGitTree* child in [self children])
162                         [child saveToFolder: newName];
163         }
166 - (NSString*) tmpDirWithContents
168         if (leaf)
169                 return nil;
171         if (!localFileName)
172                 localFileName = [PBEasyFS tmpDirWithPrefix: path];
174         for (PBGitTree* child in [self children]) {
175                 [child saveToFolder: localFileName];
176         }
177         
178         return localFileName;
181         
183 - (NSString*) tmpFileNameForContents
185         if (!leaf)
186                 return [self tmpDirWithContents];
187         
188         if ([self isLocallyCached])
189                 return localFileName;
190         
191         if (!localFileName)
192                 localFileName = [[PBEasyFS tmpDirWithPrefix: sha] stringByAppendingPathComponent:path];
193         
194         NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", [self refSpec], nil]];
195         NSData* data = [handle readDataToEndOfFile];
196         [data writeToFile:localFileName atomically:YES];
197         
198         NSFileManager* fs = [NSFileManager defaultManager];
199         localMtime = [[fs attributesOfItemAtPath:localFileName error: nil] objectForKey:NSFileModificationDate];
201         return localFileName;
204 - (NSArray*) children
206         if (children != nil)
207                 return children;
208         
209         NSString* ref = [self refSpec];
211         NSFileHandle* handle = [repository handleForArguments:[NSArray arrayWithObjects:@"show", ref, nil]];
212         [handle readLine];
213         [handle readLine];
214         
215         NSMutableArray* c = [NSMutableArray array];
216         
217         NSString* p = [handle readLine];
218         while (p.length > 0) {
219                 BOOL isLeaf = ([p characterAtIndex:p.length - 1] != '/');
220                 if (!isLeaf)
221                         p = [p substringToIndex:p.length -1];
223                 PBGitTree* child = [PBGitTree treeForTree:self andPath:p];
224                 child.leaf = isLeaf;
225                 [c addObject: child];
226                 
227                 p = [handle readLine];
228         }
229         children = c;
230         return c;
233 - (NSString*) fullPath
235         if (!parent)
236                 return @"";
237         
238         if ([parent.fullPath isEqualToString:@""])
239                 return self.path;
240         
241         return [parent.fullPath stringByAppendingPathComponent: self.path];
244 - (void) finalize
246         if (localFileName)
247                 [[NSFileManager defaultManager] removeFileAtPath:localFileName handler:nil];
248         [super finalize];
250 @end