diff --git a/SwiftGit2/Repository.swift b/SwiftGit2/Repository.swift index 3f30ede..d2ff101 100644 --- a/SwiftGit2/Repository.swift +++ b/SwiftGit2/Repository.swift @@ -540,4 +540,64 @@ final public class Repository { } return Result.success(commits) } + + public class CommitIterator: IteratorProtocol { + public typealias Element = Result + var repo: Repository + var branch: Branch + var revisionWalker: OpaquePointer? = nil + var oid: git_oid + + public init(repo: Repository, branch: Branch) { + self.repo = repo + self.branch = branch + self.oid = branch.oid.oid + setupRevisionWalker() + } + + deinit { + git_revwalk_free(self.revisionWalker) + } + + private func setupRevisionWalker() { + git_revwalk_new(&revisionWalker, repo.pointer) + git_revwalk_sorting(revisionWalker, GIT_SORT_TOPOLOGICAL.rawValue) + git_revwalk_sorting(revisionWalker, GIT_SORT_TIME.rawValue) + git_revwalk_push(revisionWalker, &oid) + } + + private func error(from gitCommands: [(key: String, value: () -> Int32)]) -> NSError? { + // TODO: Make a flatMap command that stops on first nil + let errors: [NSError] = gitCommands.flatMap { + let result = $0.value() + return result == GIT_OK.rawValue ? nil : NSError(gitError: result, pointOfFailure: $0.key) + } + if errors.count > 0 { + return errors.first! + } else { + return nil + } + } + + public func next() -> Element? { + var unsafeCommit: OpaquePointer? = nil + let gitCommands = [ + (key: "git_revwalk_next", value: { return git_revwalk_next(&self.oid, self.revisionWalker) }), + (key: "git_commit_lookup", value: { return git_commit_lookup(&unsafeCommit, self.repo.pointer, &self.oid) }) + ] + if let error = error(from: gitCommands) { + return Result.failure(error) + } else { + guard let commit = unsafeCommit else { + return nil + } + git_commit_free(unsafeCommit) + return Result.success(Commit(commit)) + } + } + } + + public func commits(in branch: Branch) -> CommitIterator { + return CommitIterator(repo: self, branch: branch) + } }