apply: Add bindings for git_apply_to_tree (#657) #665
18
diff.go
18
diff.go
|
@ -980,6 +980,24 @@ func (v *Repository) ApplyDiff(diff *Diff, location ApplyLocation, opts *ApplyOp
|
|||
return nil
|
||||
}
|
||||
|
||||
// ApplyToTree applies a Diff to a Tree and returns the resulting image as an Index.
|
||||
func (v *Repository) ApplyToTree(diff *Diff, tree *Tree, opts *ApplyOptions) (*Index, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
var indexPtr *C.git_index
|
||||
cOpts := opts.toC()
|
||||
ecode := C.git_apply_to_tree(&indexPtr, v.ptr, tree.cast_ptr, diff.ptr, cOpts)
|
||||
runtime.KeepAlive(diff)
|
||||
runtime.KeepAlive(tree)
|
||||
runtime.KeepAlive(cOpts)
|
||||
if ecode != 0 {
|
||||
return nil, MakeGitError(ecode)
|
||||
}
|
||||
|
||||
return newIndexFromC(indexPtr, v), nil
|
||||
}
|
||||
|
||||
// DiffFromBuffer reads the contents of a git patch file into a Diff object.
|
||||
//
|
||||
// The diff object produced is similar to the one that would be produced if you
|
||||
|
|
136
diff_test.go
136
diff_test.go
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -463,6 +464,141 @@ func TestApplyDiffAddfile(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestApplyToTree(t *testing.T) {
|
||||
repo := createTestRepo(t)
|
||||
defer cleanupTestRepo(t, repo)
|
||||
|
||||
seedTestRepo(t, repo)
|
||||
|
||||
commitA, treeA := addAndGetTree(t, repo, "file", "a")
|
||||
defer commitA.Free()
|
||||
defer treeA.Free()
|
||||
commitB, treeB := addAndGetTree(t, repo, "file", "b")
|
||||
defer commitB.Free()
|
||||
defer treeB.Free()
|
||||
commitC, treeC := addAndGetTree(t, repo, "file", "c")
|
||||
defer commitC.Free()
|
||||
defer treeC.Free()
|
||||
|
||||
diffAB, err := repo.DiffTreeToTree(treeA, treeB, nil)
|
||||
checkFatal(t, err)
|
||||
|
||||
diffAC, err := repo.DiffTreeToTree(treeA, treeC, nil)
|
||||
checkFatal(t, err)
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
tree *Tree
|
||||
diff *Diff
|
||||
applyHunkCallback ApplyHunkCallback
|
||||
applyDeltaCallback ApplyDeltaCallback
|
||||
error error
|
||||
expectedDiff *Diff
|
||||
}{
|
||||
{
|
||||
name: "applying patch produces the same diff",
|
||||
tree: treeA,
|
||||
diff: diffAB,
|
||||
expectedDiff: diffAB,
|
||||
},
|
||||
{
|
||||
name: "applying a conflicting patch errors",
|
||||
tree: treeB,
|
||||
diff: diffAC,
|
||||
error: &GitError{
|
||||
Message: "hunk at line 1 did not apply",
|
||||
Code: ErrApplyFail,
|
||||
Class: ErrClassPatch,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "callbacks succeeding apply the diff",
|
||||
tree: treeA,
|
||||
diff: diffAB,
|
||||
applyHunkCallback: func(*DiffHunk) (bool, error) { return true, nil },
|
||||
applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, nil },
|
||||
expectedDiff: diffAB,
|
||||
},
|
||||
{
|
||||
name: "hunk callback returning false does not apply",
|
||||
tree: treeA,
|
||||
diff: diffAB,
|
||||
applyHunkCallback: func(*DiffHunk) (bool, error) { return false, nil },
|
||||
},
|
||||
{
|
||||
name: "hunk callback erroring fails the call",
|
||||
tree: treeA,
|
||||
diff: diffAB,
|
||||
applyHunkCallback: func(*DiffHunk) (bool, error) { return true, errors.New("message dropped") },
|
||||
error: &GitError{
|
||||
Code: ErrGeneric,
|
||||
Class: ErrClassInvalid,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delta callback returning false does not apply",
|
||||
tree: treeA,
|
||||
diff: diffAB,
|
||||
applyDeltaCallback: func(*DiffDelta) (bool, error) { return false, nil },
|
||||
},
|
||||
{
|
||||
name: "delta callback erroring fails the call",
|
||||
tree: treeA,
|
||||
diff: diffAB,
|
||||
applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, errors.New("message dropped") },
|
||||
error: &GitError{
|
||||
Code: ErrGeneric,
|
||||
Class: ErrClassInvalid,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
opts, err := DefaultApplyOptions()
|
||||
checkFatal(t, err)
|
||||
|
||||
opts.ApplyHunkCallback = tc.applyHunkCallback
|
||||
opts.ApplyDeltaCallback = tc.applyDeltaCallback
|
||||
|
||||
index, err := repo.ApplyToTree(tc.diff, tc.tree, opts)
|
||||
if tc.error != nil {
|
||||
if !reflect.DeepEqual(err, tc.error) {
|
||||
t.Fatalf("expected error %q but got %q", tc.error, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
checkFatal(t, err)
|
||||
|
||||
patchedTreeOID, err := index.WriteTreeTo(repo)
|
||||
checkFatal(t, err)
|
||||
|
||||
patchedTree, err := repo.LookupTree(patchedTreeOID)
|
||||
checkFatal(t, err)
|
||||
|
||||
patchedDiff, err := repo.DiffTreeToTree(tc.tree, patchedTree, nil)
|
||||
checkFatal(t, err)
|
||||
|
||||
appliedRaw, err := patchedDiff.ToBuf(DiffFormatPatch)
|
||||
checkFatal(t, err)
|
||||
|
||||
if tc.expectedDiff == nil {
|
||||
if len(appliedRaw) > 0 {
|
||||
t.Fatalf("expected no diff but got: %s", appliedRaw)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
expectedDiff, err := tc.expectedDiff.ToBuf(DiffFormatPatch)
|
||||
checkFatal(t, err)
|
||||
|
||||
if string(expectedDiff) != string(appliedRaw) {
|
||||
t.Fatalf("diffs do not match:\nexpected: %s\n\nactual: %s", expectedDiff, appliedRaw)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// checkSecondFileStaged checks that there is a single file called "file2" uncommitted in the repo
|
||||
func checkSecondFileStaged(t *testing.T, repo *Repository) {
|
||||
opts := StatusOptions{
|
||||
|
|
3
git.go
3
git.go
|
@ -45,6 +45,7 @@ const (
|
|||
ErrClassRevert ErrorClass = C.GITERR_REVERT
|
||||
ErrClassCallback ErrorClass = C.GITERR_CALLBACK
|
||||
ErrClassRebase ErrorClass = C.GITERR_REBASE
|
||||
ErrClassPatch ErrorClass = C.GITERR_PATCH
|
||||
)
|
||||
|
||||
type ErrorCode int
|
||||
|
@ -109,6 +110,8 @@ const (
|
|||
ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH
|
||||
// Signals end of iteration with iterator
|
||||
ErrIterOver ErrorCode = C.GIT_ITEROVER
|
||||
// Patch application failed
|
||||
ErrApplyFail ErrorCode = C.GIT_EAPPLYFAIL
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
Loading…
Reference in New Issue