5 // Created by Pieter de Bie on 17-06-08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
9 #import "PBGitRevList.h"
10 #import "PBGitRepository.h"
11 #import "PBGitCommit.h"
12 #import "PBGitGrapher.h"
13 #import "PBGitRevSpecifier.h"
16 #include <ext/stdio_filebuf.h>
23 @implementation PBGitRevList
26 - initWithRepository: (id) repo
29 [repository addObserver:self forKeyPath:@"currentBranch" options:0 context:nil];
36 [self readCommitsForce: YES];
39 - (void) readCommitsForce: (BOOL) force
41 // We use refparse to get the commit sha that we will parse. That way,
42 // we can check if the current branch is the same as the previous one
43 // and in that case we don't have to reload the revision list.
45 // If no branch is selected, don't do anything
46 if (![repository currentBranch])
49 PBGitRevSpecifier* newRev = [repository currentBranch];
50 NSString* newSha = nil;
52 if (!force && newRev && [newRev isSimpleRef]) {
53 newSha = [repository parseReference:[newRev simpleRef]];
54 if ([newSha isEqualToString:lastSha])
59 NSThread * commitThread = [[NSThread alloc] initWithTarget: self selector: @selector(walkRevisionListWithSpecifier:) object:newRev];
63 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
64 change:(NSDictionary *)change context:(void *)context
66 if (object == repository)
67 [self readCommitsForce: NO];
70 - (void) walkRevisionListWithSpecifier: (PBGitRevSpecifier*) rev
72 NSDate *start = [NSDate date];
73 NSMutableArray* revisions = [NSMutableArray array];
74 PBGitGrapher* g = [[PBGitGrapher alloc] initWithRepository: repository];
75 std::map<string, NSStringEncoding> encodingMap;
77 NSMutableArray* arguments;
78 BOOL showSign = [rev hasLeftRight];
81 arguments = [NSMutableArray arrayWithObjects:@"log", @"-z", @"--early-output", @"--topo-order", @"--pretty=format:%H\01%e\01%an\01%s\01%P\01%at\01%m", nil];
83 arguments = [NSMutableArray arrayWithObjects:@"log", @"-z", @"--early-output", @"--topo-order", @"--pretty=format:%H\01%e\01%an\01%s\01%P\01%at", nil];
86 [arguments addObject:@"HEAD"];
88 [arguments addObjectsFromArray:[rev parameters]];
90 if ([rev hasPathLimiter])
91 [arguments insertObject:@"--children" atIndex:1];
93 NSTask *task = [PBEasyPipe taskForCommand:[PBGitBinary path] withArgs:arguments inDir:[repository fileURL].path];
95 NSFileHandle* handle = [task.standardOutput fileHandleForReading];
97 int fd = [handle fileDescriptor];
98 __gnu_cxx::stdio_filebuf<char> buf(fd, std::ios::in);
99 std::istream stream(&buf);
104 if (!getline(stream, sha, '\1'))
107 // We reached the end of some temporary output. Show what we have
108 // until now, and then start again. The sha of the next thing is still
109 // in this buffer. So, we use a substring of current input.
110 if (sha[1] == 'i') // Matches 'Final output'
113 [self performSelectorOnMainThread:@selector(setCommits:) withObject:revisions waitUntilDone:NO];
114 g = [[PBGitGrapher alloc] initWithRepository: repository];
115 revisions = [NSMutableArray array];
117 // If the length is < 40, then there are no commits.. quit now
118 if (sha.length() < 40)
121 sha = sha.substr(sha.length() - 40, 40);
124 // From now on, 1.2 seconds
126 getline(stream, encoding_str, '\1');
127 NSStringEncoding encoding = NSUTF8StringEncoding;
128 if (encoding_str.length())
130 if (encodingMap.find(encoding_str) != encodingMap.end()) {
131 encoding = encodingMap[encoding_str];
133 encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)[NSString stringWithUTF8String:encoding_str.c_str()]));
134 encodingMap[encoding_str] = encoding;
139 git_oid_mkstr(&oid, sha.c_str());
140 PBGitCommit* newCommit = [[PBGitCommit alloc] initWithRepository:repository andSha:oid];
143 getline(stream, author, '\1');
146 getline(stream, subject, '\1');
149 getline(stream, parentString, '\1');
150 if (parentString.size() != 0)
152 if (((parentString.size() + 1) % 41) != 0) {
153 NSLog(@"invalid parents: %i", parentString.size());
156 int nParents = (parentString.size() + 1) / 41;
157 git_oid *parents = (git_oid *)malloc(sizeof(git_oid) * nParents);
159 for (parentIndex = 0; parentIndex < nParents; ++parentIndex)
160 git_oid_mkstr(parents + parentIndex, parentString.substr(parentIndex * 41, 40).c_str());
162 newCommit.parentShas = parents;
163 newCommit.nParents = nParents;
170 [newCommit setSubject:[NSString stringWithCString:subject.c_str() encoding:encoding]];
171 [newCommit setAuthor:[NSString stringWithCString:author.c_str() encoding:encoding]];
172 [newCommit setTimestamp:time];
177 stream >> c; // Remove separator
179 if (c != '>' && c != '<' && c != '^' && c != '-')
180 NSLog(@"Error loading commits: sign not correct");
181 [newCommit setSign: c];
187 cout << "Error" << endl;
189 [revisions addObject: newCommit];
190 [g decorateCommit: newCommit];
192 if (++num % 1000 == 0)
193 [self performSelectorOnMainThread:@selector(setCommits:) withObject:revisions waitUntilDone:NO];
196 NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start];
197 NSLog(@"Loaded %i commits in %f seconds", num, duration);
198 // Make sure the commits are stored before exiting.
199 [self performSelectorOnMainThread:@selector(setCommits:) withObject:revisions waitUntilDone:YES];
200 [task waitUntilExit];