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
16 changes: 12 additions & 4 deletions applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ type Applier struct {
entries map[string]*github.TreeEntry
uncommitted bool

applyOptions []gitdiff.ApplyOption
applyOptions []gitdiff.ApplyOption
allowEmptyCommits bool
}

// NewApplier creates a new Applier for a repository. The Applier applies
Expand All @@ -53,6 +54,13 @@ func (a *Applier) SetApplyOptions(opts ...gitdiff.ApplyOption) {
a.applyOptions = opts
}

// SetAllowEmptyCommits controls whether Commit may create a commit without
// pending changes. Empty commits reuse the current tree. The default is false.
// This setting is preserved across calls to Commit and Reset.
func (a *Applier) SetAllowEmptyCommits(allow bool) {
a.allowEmptyCommits = allow
}

// Apply applies the changes in a file, adds the result to the list of pending
// tree entries, and returns the entry. If the application succeeds, Apply
// creates a blob in the repository with the modified content.
Expand Down Expand Up @@ -231,8 +239,8 @@ func (a *Applier) CreateTree(ctx context.Context) (*github.Tree, error) {

// Commit commits the latest tree, optionally using the details in tmpl and
// header. If there are pending tree entries, it calls CreateTree before
// creating the commit. It returns an error if there are no pending trees or
// tree entries.
// creating the commit. If there are no pending trees or tree entries, it returns
// an error unless empty commits are enabled with [Applier.SetAllowEmptyCommits].
//
// If tmpl is not nil, Commit uses it as a template for the new commit,
// overwriting fields as needed. If header is not nil, Commit uses it to set
Expand All @@ -243,7 +251,7 @@ func (a *Applier) CreateTree(ctx context.Context) (*github.Tree, error) {
// message, the current time, and the authenticated user as needed for the
// commit details.
func (a *Applier) Commit(ctx context.Context, tmpl *github.Commit, header *gitdiff.PatchHeader) (*github.Commit, error) {
if !a.uncommitted && len(a.entries) == 0 {
if !a.allowEmptyCommits && !a.uncommitted && len(a.entries) == 0 {
return nil, errors.New("no pending tree or tree entries")
}
if len(a.entries) > 0 {
Expand Down
29 changes: 29 additions & 0 deletions applier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,35 @@ func TestApplier(t *testing.T) {
}
}

func TestApplierSupportsEmptyCommits(t *testing.T) {
tctx := prepareTestContext(t)

t.Logf("Test ID: %s", tctx.ID)
t.Logf("Test Repository: %s", tctx.Repo.String())

createBranch(t, tctx)
defer cleanupBranches(t, tctx)

applier := NewApplier(tctx.Client, tctx.Repo, tctx.BaseCommit)
applier.SetAllowEmptyCommits(true)

commit, err := applier.Commit(tctx, nil, &gitdiff.PatchHeader{Title: "empty"})
if err != nil {
t.Fatalf("error committing changes form empty patch: %v", err)
}
// calling "Commit" on the applier without making any calls to "Apply" to apply patches should create a new empty
// commit. The returned commit should be different from the base commit, its tree should be the same.
if commit.GetSHA() == tctx.BaseCommit.GetSHA() {
t.Errorf("empty commit has same SHA as base commit: %s", commit.GetSHA())
}
if len(commit.Parents) != 1 || commit.Parents[0].GetSHA() != tctx.BaseCommit.GetSHA() {
t.Errorf("empty commit does not have base commit as its parent: %v", commit.Parents)
}
if commit.GetTree().GetSHA() != tctx.BaseTree.GetSHA() {
t.Errorf("empty commit changed tree: expected %s, actual %s", tctx.BaseTree.GetSHA(), commit.GetTree().GetSHA())
}
}

type TestContext struct {
context.Context

Expand Down
Loading