Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Sources/SwiftGitX/Models/Options/CommitOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import libgit2

/// Options for the commit operation.
public struct CommitOptions {
public static let `default` = CommitOptions()

public static let allowEmpty = CommitOptions(allowEmpty: true)

/// If true, allow creating a commit with no changes. Otherwise, fail if there are no changes. Default is false.
public let allowEmpty: Bool

public init(allowEmpty: Bool = false) {
self.allowEmpty = allowEmpty
}

var gitCommitCreateOptions: git_commit_create_options {
var options = git_commit_create_options()
options.version = UInt32(GIT_COMMIT_CREATE_OPTIONS_VERSION)
options.allow_empty_commit = allowEmpty ? 1 : 0
options.author = nil
options.committer = nil
options.message_encoding = nil

return options
}
}
9 changes: 6 additions & 3 deletions Sources/SwiftGitX/Repository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -503,23 +503,26 @@ public extension Repository {
public extension Repository {
/// Create a new commit containing the current contents of the index.
///
/// - Parameter message: The commit message.
/// - Parameters:
/// - message: The commit message.
/// - options: The options to use when creating the commit.
///
/// - Returns: The created commit.
///
/// - Throws: `RepositoryError.failedToCommit` if the commit operation fails.
///
/// This method uses the default author and committer information.
@discardableResult
func commit(message: String) throws -> Commit {
func commit(message: String, options: CommitOptions = .default) throws -> Commit {
// Create a new commit from the index
var oid = git_oid()
var gitOptions = options.gitCommitCreateOptions

let status = git_commit_create_from_stage(
&oid,
pointer,
message,
nil
&gitOptions
)

guard status == GIT_OK.rawValue else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ final class RepositoryOperationTests: SwiftGitXTestCase {
XCTAssertEqual(commit, headCommit)
}

func testEmptyCommit() throws {
// Create a new repository at the temporary directory
let repository = Repository.mock(named: "test-empty-commit", in: Self.directory)

// Create initial commit
try repository.mockCommit(message: "Initial commit")

// Verify that committing with no changes fails by default
XCTAssertThrowsError(try repository.commit(message: "Empty commit without option")) { error in
// Expect a failedToCommit error
if case RepositoryError.failedToCommit = error {
// Expected error
} else {
XCTFail("Expected failedToCommit error, got \(error)")
}
}

// Verify that committing with allowEmpty option succeeds
let emptyCommit = try repository.commit(message: "Empty commit with option", options: .allowEmpty)

// Get the HEAD commit
let headCommit = try XCTUnwrap(repository.HEAD.target as? Commit)

// Check if the HEAD commit is the same as the created empty commit
XCTAssertEqual(emptyCommit, headCommit)

// Verify the commit message
XCTAssertEqual(emptyCommit.message, "Empty commit with option")
}

func testReset() throws {
// Create a new repository at the temporary directory
let repository = Repository.mock(named: "test-reset", in: Self.directory)
Expand Down