Add functionality for converting Diff objects to a buffer and back

This commit is contained in:
Michael Boulton 2020-08-10 16:57:46 +01:00
parent 462ebd83e0
commit de24abbdae
No known key found for this signature in database
GPG Key ID: 8A62CA0BE2E0197E
2 changed files with 136 additions and 0 deletions

74
diff.go
View File

@ -3,6 +3,7 @@ package git
/*
#include <git2.h>
extern void _go_git_apply_init_options(git_apply_options *options);
extern int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload);
extern void _go_git_setup_diff_notify_callbacks(git_diff_options* opts);
extern int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload);
@ -847,3 +848,76 @@ func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string,
return nil
}
type ApplyOptions struct {
Version uint
Flags uint
// TODO: there are some more flags, not currently used
}
func DefaultApplyOptions() (*ApplyOptions, error) {
opts := C.git_apply_options{}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
C._go_git_apply_init_options(&opts)
return applyOptionsFromC(&opts), nil
}
func (a *ApplyOptions) toC() *C.git_apply_options {
if a == nil {
return nil
}
opts := &C.git_apply_options{
version: C.uint(a.Version),
flags: C.uint(a.Flags),
}
return opts
}
func applyOptionsFromC(opts *C.git_apply_options) *ApplyOptions {
return &ApplyOptions{
Version: uint(opts.version),
Flags: uint(opts.flags),
}
}
type GitApplyLocation int
const (
GitApplyLocationWorkdir GitApplyLocation = C.GIT_APPLY_LOCATION_WORKDIR
GitApplyLocationIndex GitApplyLocation = C.GIT_APPLY_LOCATION_INDEX
GitApplyLocationBoth GitApplyLocation = C.GIT_APPLY_LOCATION_BOTH
)
func (v *Repository) ApplyDiff(diff *Diff, location GitApplyLocation, opts *ApplyOptions) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ecode := C.git_apply(v.ptr, diff.ptr, C.git_apply_location_t(location), opts.toC())
runtime.KeepAlive(v)
if ecode < 0 {
return MakeGitError(ecode)
}
return nil
}
func DiffFromBuffer(buffer []byte, repo *Repository) (*Diff, error) {
var diff *C.git_diff
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ecode := C.git_diff_from_buffer(&diff, C.CString(string(buffer)), C.size_t(len(buffer)))
if ecode < 0 {
return nil, MakeGitError(ecode)
}
runtime.KeepAlive(diff)
return newDiffFromC(diff, repo), nil
}

View File

@ -236,3 +236,65 @@ func TestDiffBlobs(t *testing.T) {
t.Fatalf("Bad number of lines iterated")
}
}
func Test_ApplyDiff_Addfile(t *testing.T) {
repo := createTestRepo(t)
defer cleanupTestRepo(t, repo)
seedTestRepo(t, repo)
addFirstFileCommit, addFileTree := addAndGetTree(t, repo, "file1", `hello`)
addSecondFileCommit, addSecondFileTree := addAndGetTree(t, repo, "file2", `hello2`)
diff, err := repo.DiffTreeToTree(addFileTree, addSecondFileTree, nil)
checkFatal(t, err)
t.Run("check does not apply to current tree because file exists", func(t *testing.T) {
err = repo.ResetToCommit(addSecondFileCommit, ResetHard, &CheckoutOpts{})
checkFatal(t, err)
err = repo.ApplyDiff(diff, GitApplyLocationBoth, nil)
if err == nil {
t.Error("expecting applying patch to current repo to fail")
}
})
t.Run("check apply to correct commit", func(t *testing.T) {
err = repo.ResetToCommit(addFirstFileCommit, ResetHard, &CheckoutOpts{})
checkFatal(t, err)
err = repo.ApplyDiff(diff, GitApplyLocationBoth, nil)
checkFatal(t, err)
})
t.Run("check convert to raw buffer and apply", func(t *testing.T) {
err = repo.ResetToCommit(addFirstFileCommit, ResetHard, &CheckoutOpts{})
checkFatal(t, err)
raw, err := diff.ToBuf(DiffFormatPatch)
checkFatal(t, err)
if len(raw) == 0 {
t.Error("empty diff created")
}
diff2, err := DiffFromBuffer(raw, repo)
checkFatal(t, err)
err = repo.ApplyDiff(diff2, GitApplyLocationBoth, nil)
checkFatal(t, err)
})
}
func addAndGetTree(t *testing.T, repo *Repository, filename string, content string) (*Commit, *Tree) {
commitId, err := commitSomething(repo, filename, content)
checkFatal(t, err)
commit, err := repo.LookupCommit(commitId)
checkFatal(t, err)
tree, err := commit.Tree()
checkFatal(t, err)
return commit, tree
}