Add diff structs for parsing git_diff_deltas into Swift, more testing

This commit is contained in:
Jake Van Alstyne 🎩 2017-08-20 15:48:17 -06:00
parent c4388f0a07
commit b582e1e642
4 changed files with 237 additions and 36 deletions

View File

@ -21,6 +21,10 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
232861431F4A3A2E00276D65 /* Diffs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 232861421F4A3A2E00276D65 /* Diffs.swift */; };
232861441F4A3A2E00276D65 /* Diffs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 232861421F4A3A2E00276D65 /* Diffs.swift */; };
232861451F4A3A2E00276D65 /* Diffs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 232861421F4A3A2E00276D65 /* Diffs.swift */; };
232861461F4A3A2E00276D65 /* Diffs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 232861421F4A3A2E00276D65 /* Diffs.swift */; };
237731C71F46542B0020A3FE /* repository-with-status.zip in Resources */ = {isa = PBXBuildFile; fileRef = 237731C61F46542B0020A3FE /* repository-with-status.zip */; };
2549921B34FFC36AF8C9CD6D /* CommitIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25499A996CA7BD416620A397 /* CommitIterator.swift */; };
25499D325997CAB9BEFFCA4D /* CommitIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25499A996CA7BD416620A397 /* CommitIterator.swift */; };
@ -132,6 +136,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
232861421F4A3A2E00276D65 /* Diffs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Diffs.swift; sourceTree = "<group>"; };
237731C61F46542B0020A3FE /* repository-with-status.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = "repository-with-status.zip"; sourceTree = "<group>"; };
25499A996CA7BD416620A397 /* CommitIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommitIterator.swift; sourceTree = "<group>"; };
621E66B41C72958800A0F352 /* SwiftGit2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftGit2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -280,6 +285,7 @@
isa = PBXGroup;
children = (
BEB31F251A0D6F7A00F525B9 /* SwiftGit2 */,
BEB31F261A0D6F7A00F525B9 /* Supporting Files */,
BEB31F321A0D6F7A00F525B9 /* SwiftGit2Tests */,
BEB31FA11A0E63C100F525B9 /* Libraries */,
BEB31F411A0D75EE00F525B9 /* Configuration */,
@ -313,12 +319,12 @@
DA5914751A94579000AED74C /* Errors.swift */,
BE36354B1A632C9700D37EC8 /* Libgit2.swift */,
BE2E3BE51A31261300C67092 /* Objects.swift */,
232861421F4A3A2E00276D65 /* Diffs.swift */,
BE70B3E41A1ACB1A002C3F4E /* OID.swift */,
BE7A753E1A4A2BCC002DA7E3 /* Pointers.swift */,
BEB31F6C1A0D78F300F525B9 /* Repository.swift */,
BECB5F691A56F19900999413 /* References.swift */,
BECB5F6D1A57284700999413 /* Remotes.swift */,
BEB31F261A0D6F7A00F525B9 /* Supporting Files */,
25499A996CA7BD416620A397 /* CommitIterator.swift */,
);
path = SwiftGit2;
@ -330,6 +336,7 @@
BEB31F271A0D6F7A00F525B9 /* Info.plist */,
);
name = "Supporting Files";
path = SwiftGit2;
sourceTree = "<group>";
};
BEB31F321A0D6F7A00F525B9 /* SwiftGit2Tests */ = {
@ -752,6 +759,7 @@
622726351C84E52500C53D17 /* Credentials.swift in Sources */,
621E66A31C72958800A0F352 /* Repository.swift in Sources */,
621E66A41C72958800A0F352 /* Objects.swift in Sources */,
232861451F4A3A2E00276D65 /* Diffs.swift in Sources */,
621E66A51C72958800A0F352 /* References.swift in Sources */,
621E66A61C72958800A0F352 /* Libgit2.swift in Sources */,
621E66A71C72958800A0F352 /* Pointers.swift in Sources */,
@ -767,6 +775,7 @@
files = (
621E66BA1C72958D00A0F352 /* RepositorySpec.swift in Sources */,
621E66BB1C72958D00A0F352 /* ObjectsSpec.swift in Sources */,
232861461F4A3A2E00276D65 /* Diffs.swift in Sources */,
621E66BC1C72958D00A0F352 /* RemotesSpec.swift in Sources */,
621E66BD1C72958D00A0F352 /* FixturesSpec.swift in Sources */,
621E66BE1C72958D00A0F352 /* Fixtures.swift in Sources */,
@ -785,6 +794,7 @@
622726341C84E52500C53D17 /* Credentials.swift in Sources */,
BEB31F6D1A0D78F300F525B9 /* Repository.swift in Sources */,
BE2E3BE61A31261300C67092 /* Objects.swift in Sources */,
232861431F4A3A2E00276D65 /* Diffs.swift in Sources */,
BECB5F6A1A56F19900999413 /* References.swift in Sources */,
BE36354C1A632C9700D37EC8 /* Libgit2.swift in Sources */,
BE7A753F1A4A2BCC002DA7E3 /* Pointers.swift in Sources */,
@ -800,6 +810,7 @@
files = (
BEB31F361A0D6F7A00F525B9 /* RepositorySpec.swift in Sources */,
BE2E3BE81A31262800C67092 /* ObjectsSpec.swift in Sources */,
232861441F4A3A2E00276D65 /* Diffs.swift in Sources */,
BECB5F701A57286200999413 /* RemotesSpec.swift in Sources */,
BE14AA591A1996B70015B439 /* FixturesSpec.swift in Sources */,
BE14AA551A1984550015B439 /* Fixtures.swift in Sources */,

60
SwiftGit2/Diffs.swift Normal file
View File

@ -0,0 +1,60 @@
//
// Diffs.swift
// SwiftGit2
//
// Created by Jake Van Alstyne on 8/20/17.
// Copyright © 2017 GitHub, Inc. All rights reserved.
//
public struct GitDiffFile {
var oid: OID
var path: String
var size: Int64
var flags: UInt32
}
public enum GitDeltaStatus: Int {
case current
case indexNew
case indexModified
case indexDeleted
case indexRenamed
case indexTypeChange
case workTreeNew
case workTreeModified
case workTreeDeleted
case workTreeTypeChange
case workTreeRenamed
case workTreeUnreadable
case ignored
case conflicted
var value: UInt32 {
if self.rawValue == 0 {
return UInt32(0)
}
return UInt32(1 << (self.rawValue - 1))
}
}
public struct GitDiffDelta {
var status: GitDeltaStatus
var flags: UInt32
var oldFile: GitDiffFile
var newFile: GitDiffFile
}
public enum GitDiffFlag: Int {
case binary
case notBinary
case validId
case exists
var value: UInt32 {
if self.rawValue == 0 {
return UInt32(0)
}
return UInt32(1 << (self.rawValue - 1))
}
}

View File

@ -538,11 +538,10 @@ final public class Repository {
return iterator
}
// MARK: - Status
public func getObjectsWithStatus(for commit: Commit) -> Result<[ObjectType], NSError> {
var returnDict = [ObjectType]()
// MARK: - Diffs
public func getDiffDeltas(for commit: Commit) -> Result<[GitDiffDelta], NSError> {
/// Get the Base Tree
var unsafeBaseCommit: OpaquePointer? = nil
let unsafeBaseOid = UnsafeMutablePointer<git_oid>.allocate(capacity: 1)
git_oid_fromstr(unsafeBaseOid, commit.oid.description)
@ -552,10 +551,35 @@ final public class Repository {
}
git_commit_free(unsafeBaseCommit)
guard !commit.parents.isEmpty else {
// TODO: need to handle the initial commit
return Result.failure(NSError(gitError: 0, pointOfFailure: "getObjectsWithStatus"))
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"))
}
git_tree_free(unsafeBaseTree)
if commit.parents.isEmpty {
return self.getDiffDeltasWithNoParents(from: unwrapBaseTree)
} else if commit.parents.count == 1 {
return self.getDiffDeltasWithOneParent(from: unwrapBaseTree, in: commit)
} else {
return self.getDiffDeltasWithMultipleParents(from: unwrapBaseTree, in: commit)
}
}
private func getDiffDeltasWithNoParents(from baseTree: OpaquePointer) -> Result<[GitDiffDelta], NSError> {
var unsafeDiff: OpaquePointer? = nil
let diffResult = git_diff_tree_to_tree(&unsafeDiff, self.pointer, nil, baseTree, nil)
guard diffResult == GIT_OK.rawValue, let unwrapDiffResult = unsafeDiff else {
return Result.failure(NSError(gitError: diffResult, pointOfFailure: "git_diff_tree_to_tree"))
}
return self.processDiffDeltas(unwrapDiffResult)
}
private func getDiffDeltasWithOneParent(from baseTree: OpaquePointer,
in commit: Commit) -> Result<[GitDiffDelta], NSError> {
/// Get the Parent Tree
let parent = commit.parents[0]
var unsafeParentCommit: OpaquePointer? = nil
let unsafeParentOid = UnsafeMutablePointer<git_oid>.allocate(capacity: 1)
@ -566,13 +590,6 @@ final public class Repository {
}
git_commit_free(unsafeParentCommit)
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"))
}
git_tree_free(unsafeBaseTree)
var unsafeParentTree: OpaquePointer? = nil
let parentTreeResult = git_commit_tree(&unsafeParentTree, unwrapParentCommit)
guard parentTreeResult == GIT_OK.rawValue, let unwrapParentTree = unsafeParentTree else {
@ -581,32 +598,117 @@ final public class Repository {
git_tree_free(unsafeParentTree)
var unsafeDiff: OpaquePointer? = nil
let diffResult = git_diff_tree_to_tree(&unsafeDiff, self.pointer, unwrapBaseTree, unwrapParentTree, nil)
let diffResult = git_diff_tree_to_tree(&unsafeDiff, self.pointer, unwrapParentTree, baseTree, nil)
guard diffResult == GIT_OK.rawValue, let unwrapDiffResult = unsafeDiff else {
return Result.failure(NSError(gitError: diffResult, pointOfFailure: "git_diff_tree_to_tree"))
}
let count = git_diff_num_deltas(unwrapDiffResult)
return self.processDiffDeltas(unwrapDiffResult)
}
private func getDiffDeltasWithMultipleParents(from baseTree: OpaquePointer,
in commit: Commit) -> Result<[GitDiffDelta], NSError> {
// 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"))
}
git_tree_free(unsafeParentTree)
var unsafeDiff: OpaquePointer? = nil
let diffResult = git_diff_tree_to_tree(&unsafeDiff, self.pointer, unwrapParentTree, baseTree, nil)
guard diffResult == GIT_OK.rawValue, let unwrapDiffResult = unsafeDiff else {
return Result.failure(NSError(gitError: diffResult, pointOfFailure: "git_diff_tree_to_tree"))
}
if mergeDiff == nil {
mergeDiff = unwrapDiffResult
} else {
let mergeResult = git_diff_merge(mergeDiff, unwrapDiffResult)
guard mergeResult == GIT_OK.rawValue else {
return Result.failure(NSError(gitError: mergeResult, pointOfFailure: "git_diff_merge"))
}
}
}
return self.processDiffDeltas(mergeDiff!)
}
private func processDiffDeltas(_ diffResult: OpaquePointer) -> Result<[GitDiffDelta], NSError> {
var returnDict = [GitDiffDelta]()
let count = git_diff_num_deltas(diffResult)
for i in 0..<count {
let delta = git_diff_get_delta(unwrapDiffResult, i)
let delta = git_diff_get_delta(diffResult, i)
let oldFilePath = (delta?.pointee.old_file.path!).map(String.init(cString:))
print("Old: " + oldFilePath!)
let oldOid = OID((delta?.pointee.old_file.id)!)
let oldSize = delta?.pointee.old_file.size
let oldFlags = delta?.pointee.old_file.flags
let oldFile = GitDiffFile(oid: oldOid, path: oldFilePath!, size: oldSize!, flags: oldFlags!)
let newFilePath = (delta?.pointee.new_file.path!).map(String.init(cString:))
print("Old: " + newFilePath!)
let oldOid = delta?.pointee.old_file.id
returnDict.append(self.object(OID(oldOid!)).value!)
let newOid = OID((delta?.pointee.new_file.id)!)
let newSize = delta?.pointee.new_file.size
let newFlags = delta?.pointee.new_file.flags
let newFile = GitDiffFile(oid: newOid, path: newFilePath!, size: newSize!, flags: newFlags!)
var gitDeltaStatus = GitDeltaStatus.current
let emptyOid = OID(string: "0000000000000000000000000000000000000000")
if newOid == emptyOid {
gitDeltaStatus = GitDeltaStatus.indexDeleted
} else if oldOid == emptyOid {
gitDeltaStatus = GitDeltaStatus.indexNew
} else {
if let statusValue = delta?.pointee.status.rawValue {
if (statusValue & GitDeltaStatus.current.value) != 0 {
}
if (statusValue & GitDeltaStatus.indexModified.value) != 0 {
gitDeltaStatus = GitDeltaStatus.indexModified
}
if (statusValue & GitDeltaStatus.indexRenamed.value) != 0 {
gitDeltaStatus = GitDeltaStatus.indexRenamed
}
if (statusValue & GitDeltaStatus.indexTypeChange.value) != 0 {
gitDeltaStatus = GitDeltaStatus.indexTypeChange
}
if (statusValue & GitDeltaStatus.ignored.value) != 0 {
gitDeltaStatus = GitDeltaStatus.ignored
}
if (statusValue & GitDeltaStatus.conflicted.value) != 0 {
gitDeltaStatus = GitDeltaStatus.conflicted
}
}
}
let gitDiffDelta = GitDiffDelta(status: gitDeltaStatus,
flags: (delta?.pointee.flags)!,
oldFile: oldFile,
newFile: newFile)
returnDict.append(gitDiffDelta)
git_diff_free(OpaquePointer(delta))
}
let result = Result<[ObjectType], NSError>.success(returnDict)
let result = Result<[GitDiffDelta], NSError>.success(returnDict)
return result
}
public func getStatus(for object: ObjectType, in commit: Commit) -> String {
let returnString = ""
return returnString
}
// MARK: - Status
public func getRepositoryStatus() -> String {

View File

@ -643,20 +643,48 @@ class RepositorySpec: QuickSpec {
}
describe("Repository.getRepositoryStatus") {
it("Should not return nothing") {
let repo = Fixtures.sharedInstance.repository(named: "repository-with-status")
it("Should return accurate status") {
let repo = Fixtures.mantleRepository
let branch = repo.localBranch(named: "master").value!
expect(repo.checkout(branch, strategy: CheckoutStrategy.None)).to(haveSucceeded())
let status = repo.getRepositoryStatus()
expect(status).to(equal("A staged-file\n"))
expect(status).to(equal(""))
}
it("Should have objects with status") {
let repo = Fixtures.sharedInstance.repository(named: "repository-with-status")
it("Should have accurate delta information") {
let repo = Fixtures.mantleRepository
let branch = repo.localBranch(named: "master").value!
expect(repo.checkout(branch, strategy: CheckoutStrategy.None)).to(haveSucceeded())
let head = repo.HEAD().value!
let commit = repo.object(head.oid).value! as! Commit
let objects = repo.getObjectsWithStatus(for: commit)
let objects = repo.getDiffDeltas(for: commit)
expect(objects.value?.count).to(equal(1))
expect(objects.value?.count).to(equal(13))
}
it("Should handle initial commit well") {
let repo = Fixtures.mantleRepository
expect(repo.checkout(OID(string: "047b931bd7f5478340cef5885a6fff713005f4d6")!,
strategy: CheckoutStrategy.None)).to(haveSucceeded())
let head = repo.HEAD().value!
let initalCommit = repo.object(head.oid).value! as! Commit
let objects = repo.getDiffDeltas(for: initalCommit)
expect(objects.value?.count).to(equal(2))
}
it("Should handle merge commits well") {
let repo = Fixtures.mantleRepository
expect(repo.checkout(OID(string: "d0d9c13da5eb5f9e8cf2a9f1f6ca3bdbe975b57d")!,
strategy: CheckoutStrategy.None)).to(haveSucceeded())
let head = repo.HEAD().value!
let initalCommit = repo.object(head.oid).value! as! Commit
let objects = repo.getDiffDeltas(for: initalCommit)
expect(objects.value?.count).to(equal(20))
}
}
}