From b55ee91e3c242b3a82325c4ce51869342567e5f7 Mon Sep 17 00:00:00 2001 From: Richard Burke Date: Tue, 3 Jul 2018 22:05:05 +0100 Subject: [PATCH] Add revert functionality Closes #436 (cherry picked from commit 30c3d0ffe2118376ccbf3ff5ea1676bd7442440d) --- revert.go | 103 +++++++++++++++++++++++++++++++++++++++++++++++++ revert_test.go | 76 ++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 revert.go create mode 100644 revert_test.go diff --git a/revert.go b/revert.go new file mode 100644 index 0000000..8e8bb29 --- /dev/null +++ b/revert.go @@ -0,0 +1,103 @@ +package git + +/* +#include +*/ +import "C" +import ( + "runtime" +) + +// RevertOptions contains options for performing a revert +type RevertOptions struct { + Version uint + Mainline uint + MergeOpts MergeOptions + CheckoutOpts CheckoutOpts +} + +func (opts *RevertOptions) toC() *C.git_revert_options { + return &C.git_revert_options{ + version: C.uint(opts.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{ + Version: uint(opts.version), + 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 +} diff --git a/revert_test.go b/revert_test.go new file mode 100644 index 0000000..fcf8e43 --- /dev/null +++ b/revert_test.go @@ -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) + } +}