From b193c80dc006232c1de655c92ab12e1f15dd901a Mon Sep 17 00:00:00 2001 From: Yifei Teng Date: Tue, 1 May 2018 13:32:59 -0500 Subject: [PATCH] implement git commit --- SwiftGit2/Objects.swift | 23 ++++++++++ SwiftGit2/Repository.swift | 89 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/SwiftGit2/Objects.swift b/SwiftGit2/Objects.swift index eb55b7c..1225d97 100644 --- a/SwiftGit2/Objects.swift +++ b/SwiftGit2/Objects.swift @@ -8,6 +8,7 @@ import Foundation import libgit2 +import Result /// A git object. public protocol ObjectType { @@ -36,6 +37,14 @@ public struct Signature { /// The time zone that `time` should be interpreted relative to. public let timeZone: TimeZone + + /// Create an instance with custom name, email, dates, etc. + public init(name: String, email: String, time: Date = Date.init(), timeZone: TimeZone = TimeZone.autoupdatingCurrent) { + self.name = name + self.email = email + self.time = time + self.timeZone = timeZone + } /// Create an instance with a libgit2 `git_signature`. public init(_ signature: git_signature) { @@ -44,6 +53,20 @@ public struct Signature { time = Date(timeIntervalSince1970: TimeInterval(signature.when.time)) timeZone = TimeZone(secondsFromGMT: 60 * Int(signature.when.offset))! } + + /// Return an unsafe pointer to the `git_signature` struct. + /// Caller is responsible for freeing it with `git_signature_free`. + var unsafeSignature: Result, NSError> { + var signature: UnsafeMutablePointer? = nil + let time = git_time_t(self.time.timeIntervalSince1970) // Unix epoch time + let offset: Int32 = 0 + let signatureResult = git_signature_new(&signature, name, email, time, offset) + guard signatureResult == GIT_OK.rawValue, let signatureUnwrap = signature else { + let err = NSError(gitError: signatureResult, pointOfFailure: "git_signature_new") + return .failure(err) + } + return .success(signatureUnwrap) + } } extension Signature: Hashable { diff --git a/SwiftGit2/Repository.swift b/SwiftGit2/Repository.swift index 79fb9ea..d936b97 100644 --- a/SwiftGit2/Repository.swift +++ b/SwiftGit2/Repository.swift @@ -593,7 +593,7 @@ final public class Repository { } return .success(index!) } - + /// Stage the file(s) under the specified path. public func add(path: String) -> Result<(), NSError> { let dir = path @@ -601,15 +601,96 @@ final public class Repository { var paths = git_strarray(strings: &dirPointer, count: 1) return unsafeIndex().flatMap { index in defer { git_index_free(index) } - let add_result = git_index_add_all(index, &paths, 0, nil, nil) - guard add_result == GIT_OK.rawValue else { - let err = NSError(gitError: add_result, pointOfFailure: "git_index_add_all") + let addResult = git_index_add_all(index, &paths, 0, nil, nil) + guard addResult == GIT_OK.rawValue else { + let err = NSError(gitError: addResult, pointOfFailure: "git_index_add_all") return .failure(err) } return .success(()) } } + /// Perform a commit with arbitrary numbers of parent commits. + public func commit( + tree treeOID: git_oid, + parents: [Commit], + message: String, + signature: Signature + ) -> Result { + return unsafeIndex().flatMap { index in + defer { git_index_free(index) } + // create commit signature + return signature.unsafeSignature.flatMap { signature in + defer { git_signature_free(signature) } + var tree: OpaquePointer? = nil + var treeOIDCopy = treeOID + let lookupResult = git_tree_lookup(&tree, self.pointer, &treeOIDCopy) + guard lookupResult == GIT_OK.rawValue else { + let err = NSError(gitError: lookupResult, pointOfFailure: "git_tree_lookup") + return .failure(err) + } + defer { git_tree_free(tree) } + + var msgBuf = git_buf() + git_message_prettify(&msgBuf, message, 0, /* ascii for # */ 35) + defer { git_buf_free(&msgBuf) } + + // use HEAD as parent + var parentC: [OpaquePointer?] = [] + for parentCommit in parents { + var parent: OpaquePointer? = nil + var oid = parentCommit.oid.oid + git_commit_lookup(&parent, self.pointer, &oid) + parentC.append(parent!) + } + + let parentsContiguous = ContiguousArray(parentC) + return parentsContiguous.withUnsafeBufferPointer { unsafeBuffer in + var commitOID = git_oid() + let parentsPtr = UnsafeMutablePointer(mutating: unsafeBuffer.baseAddress) + let result = git_commit_create( + &commitOID, + self.pointer, + "HEAD", + signature, + signature, + nil, + msgBuf.ptr, + tree, + parents.count, + parentsPtr + ) + guard result == GIT_OK.rawValue else { + return .failure(NSError(gitError: result, pointOfFailure: "git_commit_create")) + } + return commit(OID(commitOID)) + } + } + } + } + + /// Perform a commit of the staged files with the specified message and signature, + /// assuming we are not doing a merge and using the current tip as the parent. + public func commit(message: String, signature: Signature) -> Result { + return unsafeIndex().flatMap { index in + defer { git_index_free(index) } + var treeOID = git_oid() + let treeResult = git_index_write_tree(&treeOID, index) + guard treeResult == GIT_OK.rawValue else { + let err = NSError(gitError: treeResult, pointOfFailure: "git_index_write_tree") + return .failure(err) + } + var parentID = git_oid() + let nameToIDResult = git_reference_name_to_id(&parentID, self.pointer, "HEAD") + guard nameToIDResult == GIT_OK.rawValue else { + return .failure(NSError(gitError: nameToIDResult, pointOfFailure: "git_reference_name_to_id")) + } + return commit(OID(parentID)).flatMap { parentCommit in + commit(tree: treeOID, parents: [parentCommit], message: message, signature: signature) + } + } + } + // MARK: - Diffs public func diff(for commit: Commit) -> Result {