Skip to content

Commit b55ee91

Browse files
rgburkelhchavez
authored andcommitted
Add revert functionality
Closes #436 (cherry picked from commit 30c3d0f)
1 parent 2fba250 commit b55ee91

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

revert.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package git
2+
3+
/*
4+
#include <git2.h>
5+
*/
6+
import "C"
7+
import (
8+
"runtime"
9+
)
10+
11+
// RevertOptions contains options for performing a revert
12+
type RevertOptions struct {
13+
Version uint
14+
Mainline uint
15+
MergeOpts MergeOptions
16+
CheckoutOpts CheckoutOpts
17+
}
18+
19+
func (opts *RevertOptions) toC() *C.git_revert_options {
20+
return &C.git_revert_options{
21+
version: C.uint(opts.Version),
22+
mainline: C.uint(opts.Mainline),
23+
merge_opts: *opts.MergeOpts.toC(),
24+
checkout_opts: *opts.CheckoutOpts.toC(),
25+
}
26+
}
27+
28+
func revertOptionsFromC(opts *C.git_revert_options) RevertOptions {
29+
return RevertOptions{
30+
Version: uint(opts.version),
31+
Mainline: uint(opts.mainline),
32+
MergeOpts: mergeOptionsFromC(&opts.merge_opts),
33+
CheckoutOpts: checkoutOptionsFromC(&opts.checkout_opts),
34+
}
35+
}
36+
37+
func freeRevertOptions(opts *C.git_revert_options) {
38+
freeCheckoutOpts(&opts.checkout_opts)
39+
}
40+
41+
// DefaultRevertOptions initialises a RevertOptions struct with default values
42+
func DefaultRevertOptions() (RevertOptions, error) {
43+
opts := C.git_revert_options{}
44+
45+
runtime.LockOSThread()
46+
defer runtime.UnlockOSThread()
47+
48+
ecode := C.git_revert_init_options(&opts, C.GIT_REVERT_OPTIONS_VERSION)
49+
if ecode < 0 {
50+
return RevertOptions{}, MakeGitError(ecode)
51+
}
52+
53+
defer freeRevertOptions(&opts)
54+
return revertOptionsFromC(&opts), nil
55+
}
56+
57+
// Revert the provided commit leaving the index updated with the results of the revert
58+
func (r *Repository) Revert(commit *Commit, revertOptions *RevertOptions) error {
59+
runtime.LockOSThread()
60+
defer runtime.UnlockOSThread()
61+
62+
var cOpts *C.git_revert_options
63+
64+
if revertOptions != nil {
65+
cOpts = revertOptions.toC()
66+
defer freeRevertOptions(cOpts)
67+
}
68+
69+
ecode := C.git_revert(r.ptr, commit.cast_ptr, cOpts)
70+
runtime.KeepAlive(r)
71+
runtime.KeepAlive(commit)
72+
73+
if ecode < 0 {
74+
return MakeGitError(ecode)
75+
}
76+
77+
return nil
78+
}
79+
80+
// RevertCommit reverts the provided commit against "ourCommit"
81+
// The returned index contains the result of the revert and should be freed
82+
func (r *Repository) RevertCommit(revertCommit *Commit, ourCommit *Commit, mainline uint, mergeOptions *MergeOptions) (*Index, error) {
83+
runtime.LockOSThread()
84+
defer runtime.UnlockOSThread()
85+
86+
var cOpts *C.git_merge_options
87+
88+
if mergeOptions != nil {
89+
cOpts = mergeOptions.toC()
90+
}
91+
92+
var index *C.git_index
93+
94+
ecode := C.git_revert_commit(&index, r.ptr, revertCommit.cast_ptr, ourCommit.cast_ptr, C.uint(mainline), cOpts)
95+
runtime.KeepAlive(revertCommit)
96+
runtime.KeepAlive(ourCommit)
97+
98+
if ecode < 0 {
99+
return nil, MakeGitError(ecode)
100+
}
101+
102+
return newIndexFromC(index, r), nil
103+
}

revert_test.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package git
2+
3+
import (
4+
"testing"
5+
)
6+
7+
const (
8+
expectedRevertedReadmeContents = "foo\n"
9+
)
10+
11+
func TestRevert(t *testing.T) {
12+
t.Parallel()
13+
repo := createTestRepo(t)
14+
defer cleanupTestRepo(t, repo)
15+
16+
seedTestRepo(t, repo)
17+
commitID, _ := updateReadme(t, repo, content)
18+
19+
commit, err := repo.LookupCommit(commitID)
20+
checkFatal(t, err)
21+
22+
revertOptions, err := DefaultRevertOptions()
23+
checkFatal(t, err)
24+
25+
err = repo.Revert(commit, &revertOptions)
26+
checkFatal(t, err)
27+
28+
actualReadmeContents := readReadme(t, repo)
29+
30+
if actualReadmeContents != expectedRevertedReadmeContents {
31+
t.Fatalf(`README has incorrect contents after revert. Expected: "%v", Actual: "%v"`,
32+
expectedRevertedReadmeContents, actualReadmeContents)
33+
}
34+
35+
state := repo.State()
36+
if state != RepositoryStateRevert {
37+
t.Fatalf("Incorrect repository state. Expected: %v, Actual: %v", RepositoryStateRevert, state)
38+
}
39+
40+
err = repo.StateCleanup()
41+
checkFatal(t, err)
42+
43+
state = repo.State()
44+
if state != RepositoryStateNone {
45+
t.Fatalf("Incorrect repository state. Expected: %v, Actual: %v", RepositoryStateNone, state)
46+
}
47+
}
48+
49+
func TestRevertCommit(t *testing.T) {
50+
t.Parallel()
51+
repo := createTestRepo(t)
52+
defer cleanupTestRepo(t, repo)
53+
54+
seedTestRepo(t, repo)
55+
commitID, _ := updateReadme(t, repo, content)
56+
57+
commit, err := repo.LookupCommit(commitID)
58+
checkFatal(t, err)
59+
60+
revertOptions, err := DefaultRevertOptions()
61+
checkFatal(t, err)
62+
63+
index, err := repo.RevertCommit(commit, commit, 0, &revertOptions.MergeOpts)
64+
checkFatal(t, err)
65+
defer index.Free()
66+
67+
err = repo.CheckoutIndex(index, &revertOptions.CheckoutOpts)
68+
checkFatal(t, err)
69+
70+
actualReadmeContents := readReadme(t, repo)
71+
72+
if actualReadmeContents != expectedRevertedReadmeContents {
73+
t.Fatalf(`README has incorrect contents after revert. Expected: "%v", Actual: "%v"`,
74+
expectedRevertedReadmeContents, actualReadmeContents)
75+
}
76+
}

0 commit comments

Comments
 (0)