Skip to content

Add revert functionality #446

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 23, 2020
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
101 changes: 101 additions & 0 deletions revert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package git

/*
#include <git2.h>
*/
import "C"
import (
"runtime"
)

// RevertOptions contains options for performing a revert
type RevertOptions struct {
Mainline uint
MergeOpts MergeOptions
CheckoutOpts CheckoutOpts
}

func (opts *RevertOptions) toC() *C.git_revert_options {
return &C.git_revert_options{
version: C.GIT_REVERT_OPTIONS_VERSION,
mainline: C.uint(opts.Mainline),
merge_opts: *opts.MergeOpts.toC(),
checkout_opts: *opts.CheckoutOpts.toC(),
}
}

func revertOptionsFromC(opts *C.git_revert_options) RevertOptions {
return RevertOptions{
Mainline: uint(opts.mainline),
MergeOpts: mergeOptionsFromC(&opts.merge_opts),
CheckoutOpts: checkoutOptionsFromC(&opts.checkout_opts),
}
}

func freeRevertOptions(opts *C.git_revert_options) {
freeCheckoutOpts(&opts.checkout_opts)
}

// DefaultRevertOptions initialises a RevertOptions struct with default values
func DefaultRevertOptions() (RevertOptions, error) {
opts := C.git_revert_options{}

runtime.LockOSThread()
defer runtime.UnlockOSThread()

ecode := C.git_revert_init_options(&opts, C.GIT_REVERT_OPTIONS_VERSION)
if ecode < 0 {
return RevertOptions{}, MakeGitError(ecode)
}

defer freeRevertOptions(&opts)
return revertOptionsFromC(&opts), nil
}

// Revert the provided commit leaving the index updated with the results of the revert
func (r *Repository) Revert(commit *Commit, revertOptions *RevertOptions) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()

var cOpts *C.git_revert_options

if revertOptions != nil {
cOpts = revertOptions.toC()
defer freeRevertOptions(cOpts)
}

ecode := C.git_revert(r.ptr, commit.cast_ptr, cOpts)
runtime.KeepAlive(r)
runtime.KeepAlive(commit)

if ecode < 0 {
return MakeGitError(ecode)
}

return nil
}

// RevertCommit reverts the provided commit against "ourCommit"
// The returned index contains the result of the revert and should be freed
func (r *Repository) RevertCommit(revertCommit *Commit, ourCommit *Commit, mainline uint, mergeOptions *MergeOptions) (*Index, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()

var cOpts *C.git_merge_options

if mergeOptions != nil {
cOpts = mergeOptions.toC()
}

var index *C.git_index

ecode := C.git_revert_commit(&index, r.ptr, revertCommit.cast_ptr, ourCommit.cast_ptr, C.uint(mainline), cOpts)
runtime.KeepAlive(revertCommit)
runtime.KeepAlive(ourCommit)

if ecode < 0 {
return nil, MakeGitError(ecode)
}

return newIndexFromC(index, r), nil
}
76 changes: 76 additions & 0 deletions revert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package git

import (
"testing"
)

const (
expectedRevertedReadmeContents = "foo\n"
)

func TestRevert(t *testing.T) {
t.Parallel()
repo := createTestRepo(t)
defer cleanupTestRepo(t, repo)

seedTestRepo(t, repo)
commitID, _ := updateReadme(t, repo, content)

commit, err := repo.LookupCommit(commitID)
checkFatal(t, err)

revertOptions, err := DefaultRevertOptions()
checkFatal(t, err)

err = repo.Revert(commit, &revertOptions)
checkFatal(t, err)

actualReadmeContents := readReadme(t, repo)

if actualReadmeContents != expectedRevertedReadmeContents {
t.Fatalf(`README has incorrect contents after revert. Expected: "%v", Actual: "%v"`,
expectedRevertedReadmeContents, actualReadmeContents)
}

state := repo.State()
if state != RepositoryStateRevert {
t.Fatalf("Incorrect repository state. Expected: %v, Actual: %v", RepositoryStateRevert, state)
}

err = repo.StateCleanup()
checkFatal(t, err)

state = repo.State()
if state != RepositoryStateNone {
t.Fatalf("Incorrect repository state. Expected: %v, Actual: %v", RepositoryStateNone, state)
}
}

func TestRevertCommit(t *testing.T) {
t.Parallel()
repo := createTestRepo(t)
defer cleanupTestRepo(t, repo)

seedTestRepo(t, repo)
commitID, _ := updateReadme(t, repo, content)

commit, err := repo.LookupCommit(commitID)
checkFatal(t, err)

revertOptions, err := DefaultRevertOptions()
checkFatal(t, err)

index, err := repo.RevertCommit(commit, commit, 0, &revertOptions.MergeOpts)
checkFatal(t, err)
defer index.Free()

err = repo.CheckoutIndex(index, &revertOptions.CheckoutOpts)
checkFatal(t, err)

actualReadmeContents := readReadme(t, repo)

if actualReadmeContents != expectedRevertedReadmeContents {
t.Fatalf(`README has incorrect contents after revert. Expected: "%v", Actual: "%v"`,
expectedRevertedReadmeContents, actualReadmeContents)
}
}