simplify things by extracting difficult to read libgit2 interactions into helper functions and nest the various Diff structs inside a struct named Diff

This commit is contained in:
Jake Van Alstyne 🎩 2017-09-08 17:10:06 -06:00
parent 0d56e1b1ce
commit 3b9beac923
2 changed files with 156 additions and 121 deletions

View File

@ -7,75 +7,77 @@
//
import libgit2
public struct DiffFile {
public var oid: OID
public var path: String
public var size: Int64
public var flags: UInt32
public struct Diff {
public struct File {
public var oid: OID
public var path: String
public var size: Int64
public var flags: Flags
public init(from diffFile: git_diff_file) {
self.oid = OID(diffFile.id)
let path = diffFile.path
self.path = path.map(String.init(cString:))!
self.size = diffFile.size
self.flags = diffFile.flags
}
}
public struct StatusEntry {
public var status: Status?
public var headToIndex: DiffDelta?
public var indexToWorkDir: DiffDelta?
}
public struct Status: OptionSet {
// This appears to be necessary due to bug in Swift
// https://bugs.swift.org/browse/SR-3003
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public let rawValue: UInt32
public static let current = Status(rawValue: 0)
public static let indexNew = Status(rawValue: 1 << 0)
public static let indexModified = Status(rawValue: 1 << 1)
public static let indexDeleted = Status(rawValue: 1 << 2)
public static let indexRenamed = Status(rawValue: 1 << 3)
public static let indexTypeChange = Status(rawValue: 1 << 4)
public static let workTreeNew = Status(rawValue: 1 << 5)
public static let workTreeModified = Status(rawValue: 1 << 6)
public static let workTreeDeleted = Status(rawValue: 1 << 7)
public static let workTreeTypeChange = Status(rawValue: 1 << 8)
public static let workTreeRenamed = Status(rawValue: 1 << 9)
public static let workTreeUnreadable = Status(rawValue: 1 << 10)
public static let ignored = Status(rawValue: 1 << 11)
public static let conflicted = Status(rawValue: 1 << 12)
}
public struct DiffFlag: OptionSet {
// This appears to be necessary due to bug in Swift
// https://bugs.swift.org/browse/SR-3003
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public let rawValue: UInt32
public static let binary = DiffFlag(rawValue: 0)
public static let notBinary = DiffFlag(rawValue: 1 << 0)
public static let validId = DiffFlag(rawValue: 1 << 1)
public static let exists = DiffFlag(rawValue: 1 << 2)
}
public struct DiffDelta {
public var status: Status?
public var flags: DiffFlag?
public var oldFile: DiffFile?
public var newFile: DiffFile?
public init(from diffDelta: git_diff_delta) {
self.status = Status(rawValue: diffDelta.status.rawValue)
self.flags = DiffFlag(rawValue: diffDelta.flags)
self.oldFile = DiffFile(from: diffDelta.old_file)
self.newFile = DiffFile(from: diffDelta.new_file)
public init(from diffFile: git_diff_file) {
self.oid = OID(diffFile.id)
let path = diffFile.path
self.path = path.map(String.init(cString:))!
self.size = diffFile.size
self.flags = Flags(rawValue: diffFile.flags)
}
}
public struct StatusEntry {
public var status: Status
public var headToIndex: Delta?
public var indexToWorkDir: Delta?
}
public struct Status: OptionSet {
// This appears to be necessary due to bug in Swift
// https://bugs.swift.org/browse/SR-3003
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public let rawValue: UInt32
public static let current = Status(rawValue: 0)
public static let indexNew = Status(rawValue: 1 << 0)
public static let indexModified = Status(rawValue: 1 << 1)
public static let indexDeleted = Status(rawValue: 1 << 2)
public static let indexRenamed = Status(rawValue: 1 << 3)
public static let indexTypeChange = Status(rawValue: 1 << 4)
public static let workTreeNew = Status(rawValue: 1 << 5)
public static let workTreeModified = Status(rawValue: 1 << 6)
public static let workTreeDeleted = Status(rawValue: 1 << 7)
public static let workTreeTypeChange = Status(rawValue: 1 << 8)
public static let workTreeRenamed = Status(rawValue: 1 << 9)
public static let workTreeUnreadable = Status(rawValue: 1 << 10)
public static let ignored = Status(rawValue: 1 << 11)
public static let conflicted = Status(rawValue: 1 << 12)
}
public struct Flags: OptionSet {
// This appears to be necessary due to bug in Swift
// https://bugs.swift.org/browse/SR-3003
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public let rawValue: UInt32
public static let binary = Flags(rawValue: 0)
public static let notBinary = Flags(rawValue: 1 << 0)
public static let validId = Flags(rawValue: 1 << 1)
public static let exists = Flags(rawValue: 1 << 2)
}
public struct Delta {
public var status: Status
public var flags: Flags
public var oldFile: File?
public var newFile: File?
public init(from delta: git_diff_delta) {
self.status = Status(rawValue: delta.status.rawValue)
self.flags = Flags(rawValue: delta.flags)
self.oldFile = File(from: delta.old_file)
self.newFile = File(from: delta.new_file)
}
}
}

View File

@ -540,63 +540,50 @@ final public class Repository {
// MARK: - Diffs
public func diff(for commit: Commit) -> Result<[DiffDelta], NSError> {
public func diff(for commit: Commit) -> Result<[Diff.Delta], NSError> {
/// Get the Base Tree
var unsafeBaseCommit: OpaquePointer? = nil
let unsafeBaseOid = UnsafeMutablePointer<git_oid>.allocate(capacity: 1)
git_oid_fromstr(unsafeBaseOid, commit.oid.description)
let lookupBaseGitResult = git_commit_lookup(&unsafeBaseCommit, self.pointer, unsafeBaseOid)
guard lookupBaseGitResult == GIT_OK.rawValue, let unwrapBaseCommit = unsafeBaseCommit else {
return Result.failure(NSError(gitError: lookupBaseGitResult, pointOfFailure: "git_commit_lookup"))
let baseCommit = self.commit(with: commit.oid)
guard baseCommit.error == nil else {
return Result.failure(baseCommit.error!)
}
git_commit_free(unsafeBaseCommit)
var unsafeBaseTree: OpaquePointer? = nil
let baseTreeResult = git_commit_tree(&unsafeBaseTree, unwrapBaseCommit)
guard baseTreeResult == GIT_OK.rawValue, let unwrapBaseTree = unsafeBaseTree else {
return Result.failure(NSError(gitError: baseTreeResult, pointOfFailure: "git_commit_tree"))
let baseTree = self.tree(from: baseCommit.value!)
guard baseTree.error == nil else {
return Result.failure(baseTree.error!)
}
git_tree_free(unsafeBaseTree)
if commit.parents.isEmpty {
// Initial commit in a repository
var unsafeDiff: OpaquePointer? = nil
let diffResult = git_diff_tree_to_tree(&unsafeDiff, self.pointer, nil, unwrapBaseTree, nil)
guard diffResult == GIT_OK.rawValue, let unwrapDiffResult = unsafeDiff else {
return Result.failure(NSError(gitError: diffResult, pointOfFailure: "git_diff_tree_to_tree"))
let diffResult = self.diff(withOldTree: nil, andNewTree: baseTree.value)
guard diffResult.error == nil else {
return Result.failure(diffResult.error!)
}
return self.processDiffDeltas(unwrapDiffResult)
return self.processDiffDeltas(diffResult.value!)
} else {
// Possible Merge Commit, merge diffs of base with each parent
var mergeDiff: OpaquePointer? = nil
for parent in commit.parents {
var unsafeParentCommit: OpaquePointer? = nil
let unsafeParentOid = UnsafeMutablePointer<git_oid>.allocate(capacity: 1)
git_oid_fromstr(unsafeParentOid, parent.oid.description)
let lookupParentGitResult = git_commit_lookup(&unsafeParentCommit, self.pointer, unsafeParentOid)
guard lookupParentGitResult == GIT_OK.rawValue, let unwrapParentCommit = unsafeParentCommit else {
return Result.failure(NSError(gitError: lookupParentGitResult, pointOfFailure: "git_commit_lookup"))
}
git_commit_free(unsafeParentCommit)
var unsafeParentTree: OpaquePointer? = nil
let parentTreeResult = git_commit_tree(&unsafeParentTree, unwrapParentCommit)
guard parentTreeResult == GIT_OK.rawValue, let unwrapParentTree = unsafeParentTree else {
return Result.failure(NSError(gitError: parentTreeResult, pointOfFailure: "git_commit_tree"))
let parentCommit = self.parentCommit(from: parent)
guard parentCommit.error == nil else {
return Result.failure(parentCommit.error!)
}
git_tree_free(unsafeParentTree)
var unsafeDiff: OpaquePointer? = nil
let diffResult = git_diff_tree_to_tree(&unsafeDiff, self.pointer, unwrapParentTree, unwrapBaseTree, nil)
guard diffResult == GIT_OK.rawValue, let unwrapDiffResult = unsafeDiff else {
return Result.failure(NSError(gitError: diffResult, pointOfFailure: "git_diff_tree_to_tree"))
let parentTree = self.tree(from: parentCommit.value!)
guard parentTree.error == nil else {
return Result.failure(parentTree.error!)
}
let diffResult = self.diff(withOldTree: parentTree.value!, andNewTree: baseTree.value!)
guard diffResult.error == nil else {
return Result.failure(diffResult.error!)
}
if mergeDiff == nil {
mergeDiff = unwrapDiffResult
mergeDiff = diffResult.value
} else {
let mergeResult = git_diff_merge(mergeDiff, unwrapDiffResult)
let mergeResult = git_diff_merge(mergeDiff, diffResult.value)
guard mergeResult == GIT_OK.rawValue else {
return Result.failure(NSError(gitError: mergeResult, pointOfFailure: "git_diff_merge"))
}
@ -606,28 +593,77 @@ final public class Repository {
}
}
private func processDiffDeltas(_ diffResult: OpaquePointer) -> Result<[DiffDelta], NSError> {
var returnDict = [DiffDelta]()
private func commit(with oid: OID) -> Result<OpaquePointer, NSError> {
var unsafeBaseCommit: OpaquePointer? = nil
let unsafeBaseOid = UnsafeMutablePointer<git_oid>.allocate(capacity: 1)
git_oid_fromstr(unsafeBaseOid, oid.description)
let lookupBaseGitResult = git_commit_lookup(&unsafeBaseCommit, self.pointer, unsafeBaseOid)
guard lookupBaseGitResult == GIT_OK.rawValue, let unwrapBaseCommit = unsafeBaseCommit else {
return Result.failure(NSError(gitError: lookupBaseGitResult, pointOfFailure: "git_commit_lookup"))
}
git_commit_free(unsafeBaseCommit)
return Result.success(unwrapBaseCommit)
}
private func diff(withOldTree oldTree: OpaquePointer?,
andNewTree newTree: OpaquePointer?) -> Result<OpaquePointer, NSError> {
var unsafeDiff: OpaquePointer? = nil
let diffResult = git_diff_tree_to_tree(&unsafeDiff, self.pointer, oldTree, newTree, nil)
guard diffResult == GIT_OK.rawValue, let unwrapDiffResult = unsafeDiff else {
return Result.failure(NSError(gitError: diffResult, pointOfFailure: "git_diff_tree_to_tree"))
}
return Result.success(unwrapDiffResult)
}
private func parentCommit(from parent: PointerTo<Commit>) -> Result<OpaquePointer, NSError> {
var unsafeParentCommit: OpaquePointer? = nil
let unsafeParentOid = UnsafeMutablePointer<git_oid>.allocate(capacity: 1)
git_oid_fromstr(unsafeParentOid, parent.oid.description)
let lookupParentGitResult = git_commit_lookup(&unsafeParentCommit, self.pointer, unsafeParentOid)
guard lookupParentGitResult == GIT_OK.rawValue, let unwrapParentCommit = unsafeParentCommit else {
return Result.failure(NSError(gitError: lookupParentGitResult, pointOfFailure: "git_commit_lookup"))
}
git_commit_free(unsafeParentCommit)
return Result.success(unwrapParentCommit)
}
private func tree(from commit: OpaquePointer) -> Result<OpaquePointer, NSError> {
var unsafeTree: OpaquePointer? = nil
let treeResult = git_commit_tree(&unsafeTree, commit)
guard treeResult == GIT_OK.rawValue, let unwrapTree = unsafeTree else {
return Result.failure(NSError(gitError: treeResult, pointOfFailure: "git_commit_tree"))
}
git_tree_free(unsafeTree)
return Result.success(unwrapTree)
}
private func processDiffDeltas(_ diffResult: OpaquePointer) -> Result<[Diff.Delta], NSError> {
typealias Delta = Diff.Delta
var returnDict = [Delta]()
let count = git_diff_num_deltas(diffResult)
for i in 0..<count {
let delta = git_diff_get_delta(diffResult, i)
let gitDiffDelta = DiffDelta(from: (delta?.pointee)!)
let gitDiffDelta = Diff.Delta(from: (delta?.pointee)!)
returnDict.append(gitDiffDelta)
git_diff_free(OpaquePointer(delta))
}
let result = Result<[DiffDelta], NSError>.success(returnDict)
let result = Result<[Diff.Delta], NSError>.success(returnDict)
return result
}
// MARK: - Status
public func status() -> Result<[StatusEntry], NSError> {
public func status() -> Result<[Diff.StatusEntry], NSError> {
typealias StatusEntry = Diff.StatusEntry
var returnArray = [StatusEntry]()
// Do this because GIT_STATUS_OPTIONS_INIT is unavailable in swift
@ -652,24 +688,21 @@ final public class Repository {
if s?.pointee.status.rawValue == GIT_STATUS_CURRENT.rawValue {
continue
}
var status: Status? = nil
var headToIndex: DiffDelta? = nil
var indexToWorkDir: DiffDelta? = nil
var headToIndex: Diff.Delta? = nil
var indexToWorkDir: Diff.Delta? = nil
if let statusValue = s?.pointee.status.rawValue {
status = Status(rawValue: statusValue)
}
let status = Diff.Status(rawValue: (s?.pointee.status.rawValue)!)
if let htoi = s?.pointee.head_to_index {
headToIndex = DiffDelta(from: htoi.pointee)
headToIndex = Diff.Delta(from: htoi.pointee)
}
if let itow = s?.pointee.index_to_workdir {
indexToWorkDir = DiffDelta(from: itow.pointee)
indexToWorkDir = Diff.Delta(from: itow.pointee)
}
let statusEntry = StatusEntry(status: status, headToIndex: headToIndex, indexToWorkDir: indexToWorkDir)
let statusEntry = Diff.StatusEntry(status: status, headToIndex: headToIndex, indexToWorkDir: indexToWorkDir)
returnArray.append(statusEntry)
}