mirror of
https://github.com/gosticks/SwiftGit2.git
synced 2025-10-16 11:55:34 +00:00
Add diff structs for parsing git_diff_deltas into Swift, more testing
This commit is contained in:
parent
c4388f0a07
commit
b582e1e642
@ -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
60
SwiftGit2/Diffs.swift
Normal 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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
|
||||
@ -643,24 +643,52 @@ 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func temporaryURL(forPurpose purpose: String) -> URL {
|
||||
let globallyUniqueString = ProcessInfo.processInfo.globallyUniqueString
|
||||
let path = "\(NSTemporaryDirectory())\(globallyUniqueString)_\(purpose)"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user