summaryrefslogtreecommitdiff
path: root/diff_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'diff_test.go')
-rw-r--r--diff_test.go307
1 files changed, 307 insertions, 0 deletions
diff --git a/diff_test.go b/diff_test.go
index 6fbad51..394a4c1 100644
--- a/diff_test.go
+++ b/diff_test.go
@@ -2,6 +2,9 @@ package git
import (
"errors"
+ "fmt"
+ "io/ioutil"
+ "path"
"strings"
"testing"
)
@@ -236,3 +239,307 @@ func TestDiffBlobs(t *testing.T) {
t.Fatalf("Bad number of lines iterated")
}
}
+
+func TestApplyDiffAddfile(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, ApplyLocationBoth, 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, ApplyLocationBoth, nil)
+ checkFatal(t, err)
+
+ t.Run("Check that diff only changed one file", func(t *testing.T) {
+ checkSecondFileStaged(t, repo)
+
+ index, err := repo.Index()
+ checkFatal(t, err)
+ defer index.Free()
+
+ newTreeOID, err := index.WriteTreeTo(repo)
+ checkFatal(t, err)
+
+ newTree, err := repo.LookupTree(newTreeOID)
+ checkFatal(t, err)
+ defer newTree.Free()
+
+ _, err = repo.CreateCommit("HEAD", signature(), signature(), fmt.Sprintf("patch apply"), newTree, addFirstFileCommit)
+ checkFatal(t, err)
+ })
+
+ t.Run("test applying patch produced the same diff", func(t *testing.T) {
+ head, err := repo.Head()
+ checkFatal(t, err)
+
+ commit, err := repo.LookupCommit(head.Target())
+ checkFatal(t, err)
+
+ tree, err := commit.Tree()
+ checkFatal(t, err)
+
+ newDiff, err := repo.DiffTreeToTree(addFileTree, tree, nil)
+ checkFatal(t, err)
+
+ raw1b, err := diff.ToBuf(DiffFormatPatch)
+ checkFatal(t, err)
+ raw2b, err := newDiff.ToBuf(DiffFormatPatch)
+ checkFatal(t, err)
+
+ raw1 := string(raw1b)
+ raw2 := string(raw2b)
+
+ if raw1 != raw2 {
+ t.Error("diffs should be the same")
+ }
+ })
+ })
+
+ 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, ApplyLocationBoth, nil)
+ checkFatal(t, err)
+ })
+
+ t.Run("check apply callbacks work", func(t *testing.T) {
+ // reset the state and get new default options for test
+ resetAndGetOpts := func(t *testing.T) *ApplyOptions {
+ err = repo.ResetToCommit(addFirstFileCommit, ResetHard, &CheckoutOpts{})
+ checkFatal(t, err)
+
+ opts, err := DefaultApplyOptions()
+ checkFatal(t, err)
+
+ return opts
+ }
+
+ t.Run("Check hunk callback working applies patch", func(t *testing.T) {
+ opts := resetAndGetOpts(t)
+
+ called := false
+ opts.ApplyHunkCallback = func(hunk *DiffHunk) (apply bool, err error) {
+ called = true
+ return true, nil
+ }
+
+ err = repo.ApplyDiff(diff, ApplyLocationBoth, opts)
+ checkFatal(t, err)
+
+ if called == false {
+ t.Error("apply hunk callback was not called")
+ }
+
+ checkSecondFileStaged(t, repo)
+ })
+
+ t.Run("Check delta callback working applies patch", func(t *testing.T) {
+ opts := resetAndGetOpts(t)
+
+ called := false
+ opts.ApplyDeltaCallback = func(hunk *DiffDelta) (apply bool, err error) {
+ if hunk.NewFile.Path != "file2" {
+ t.Error("Unexpected delta in diff application")
+ }
+ called = true
+ return true, nil
+ }
+
+ err = repo.ApplyDiff(diff, ApplyLocationBoth, opts)
+ checkFatal(t, err)
+
+ if called == false {
+ t.Error("apply hunk callback was not called")
+ }
+
+ checkSecondFileStaged(t, repo)
+ })
+
+ t.Run("Check delta callback returning false does not apply patch", func(t *testing.T) {
+ opts := resetAndGetOpts(t)
+
+ called := false
+ opts.ApplyDeltaCallback = func(hunk *DiffDelta) (apply bool, err error) {
+ if hunk.NewFile.Path != "file2" {
+ t.Error("Unexpected hunk in diff application")
+ }
+ called = true
+ return false, nil
+ }
+
+ err = repo.ApplyDiff(diff, ApplyLocationBoth, opts)
+ checkFatal(t, err)
+
+ if called == false {
+ t.Error("apply hunk callback was not called")
+ }
+
+ checkNoFilesStaged(t, repo)
+ })
+
+ t.Run("Check hunk callback returning causes application to fail", func(t *testing.T) {
+ opts := resetAndGetOpts(t)
+
+ called := false
+ opts.ApplyHunkCallback = func(hunk *DiffHunk) (apply bool, err error) {
+ called = true
+ return false, errors.New("something happened")
+ }
+
+ err = repo.ApplyDiff(diff, ApplyLocationBoth, opts)
+ if err == nil {
+ t.Error("expected an error after trying to apply")
+ }
+
+ if called == false {
+ t.Error("apply hunk callback was not called")
+ }
+
+ checkNoFilesStaged(t, repo)
+ })
+
+ t.Run("Check delta callback returning causes application to fail", func(t *testing.T) {
+ opts := resetAndGetOpts(t)
+
+ called := false
+ opts.ApplyDeltaCallback = func(hunk *DiffDelta) (apply bool, err error) {
+ if hunk.NewFile.Path != "file2" {
+ t.Error("Unexpected delta in diff application")
+ }
+ called = true
+ return false, errors.New("something happened")
+ }
+
+ err = repo.ApplyDiff(diff, ApplyLocationBoth, opts)
+ if err == nil {
+ t.Error("expected an error after trying to apply")
+ }
+
+ if called == false {
+ t.Error("apply hunk callback was not called")
+ }
+
+ checkNoFilesStaged(t, repo)
+ })
+ })
+}
+
+// checkSecondFileStaged checks that there is a single file called "file2" uncommitted in the repo
+func checkSecondFileStaged(t *testing.T, repo *Repository) {
+ opts := StatusOptions{
+ Show: StatusShowIndexAndWorkdir,
+ Flags: StatusOptIncludeUntracked,
+ }
+
+ statuses, err := repo.StatusList(&opts)
+ checkFatal(t, err)
+
+ count, err := statuses.EntryCount()
+ checkFatal(t, err)
+
+ if count != 1 {
+ t.Error("diff should affect exactly one file")
+ }
+ if count == 0 {
+ t.Fatal("no statuses, cannot continue test")
+ }
+
+ entry, err := statuses.ByIndex(0)
+ checkFatal(t, err)
+
+ if entry.Status != StatusIndexNew {
+ t.Error("status should be 'new' as file has been added between commits")
+ }
+
+ if entry.HeadToIndex.NewFile.Path != "file2" {
+ t.Error("new file should be 'file2")
+ }
+ return
+}
+
+// checkNoFilesStaged checks that there is a single file called "file2" uncommitted in the repo
+func checkNoFilesStaged(t *testing.T, repo *Repository) {
+ opts := StatusOptions{
+ Show: StatusShowIndexAndWorkdir,
+ Flags: StatusOptIncludeUntracked,
+ }
+
+ statuses, err := repo.StatusList(&opts)
+ checkFatal(t, err)
+
+ count, err := statuses.EntryCount()
+ checkFatal(t, err)
+
+ if count != 0 {
+ t.Error("files changed unexpectedly")
+ }
+}
+
+// addAndGetTree creates a file and commits it, returning the commit and tree
+func addAndGetTree(t *testing.T, repo *Repository, filename string, content string) (*Commit, *Tree) {
+ headCommit, err := headCommit(repo)
+ checkFatal(t, err)
+ defer headCommit.Free()
+
+ p := repo.Path()
+ p = strings.TrimSuffix(p, ".git")
+ p = strings.TrimSuffix(p, ".git/")
+
+ err = ioutil.WriteFile(path.Join(p, filename), []byte((content)), 0777)
+ checkFatal(t, err)
+
+ index, err := repo.Index()
+ checkFatal(t, err)
+ defer index.Free()
+
+ err = index.AddByPath(filename)
+ checkFatal(t, err)
+
+ newTreeOID, err := index.WriteTreeTo(repo)
+ checkFatal(t, err)
+
+ newTree, err := repo.LookupTree(newTreeOID)
+ checkFatal(t, err)
+ defer newTree.Free()
+
+ commitId, err := repo.CreateCommit("HEAD", signature(), signature(), fmt.Sprintf("add %s", filename), newTree, headCommit)
+ checkFatal(t, err)
+
+ commit, err := repo.LookupCommit(commitId)
+ checkFatal(t, err)
+
+ tree, err := commit.Tree()
+ checkFatal(t, err)
+
+ return commit, tree
+}