From d0b334b24409ddc190a7010be0072d87df6b6bfe Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 20 Mar 2014 21:56:41 -0700 Subject: cleanup and refactor diff / patch --- diff_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 diff_test.go (limited to 'diff_test.go') diff --git a/diff_test.go b/diff_test.go new file mode 100644 index 0000000..15aabc5 --- /dev/null +++ b/diff_test.go @@ -0,0 +1,41 @@ +package git + +import ( + "testing" +) + +func TestDiffTreeToTree(t *testing.T) { + repo := createTestRepo(t) + defer repo.Free() + defer os.RemoveAll(repo.Workdir()) + + _, originalTreeId := seedTestRepo(t, repo) + originalTree, err := repo.LookupTree(originalTreeId) + + checkFatal(t, err) + updateReadme(t, repo, "file changed\n") + + _, newTreeId := seedTestRepo(t, repo) + newTree, err := repo.LookupTree(newTreeId) + checkFatal(t, err) + + diff, err := repo.DiffTreeToTree(originalTreeId, newTreeId) + checkFatal(t, err) + + files := make([]string, 0) + + err := diff.ForEachFile(func(file *DiffFile) error { + files = append(files, file.Path) + return nil + }) + + checkFatal(t, err) + + if len(files) != 0 { + t.Fatal("Incorrect number of files in diff") + } + + if files[0] != "README" { + t.Fatal("File in diff was expected to be README") + } +} -- cgit v1.2.3 From d78036fe24c9c746d3513f9e97f463f995aef0fd Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 20 Mar 2014 22:54:18 -0700 Subject: refactor and cleanup code --- diff.go | 41 +++++++++++++++++++++++------------------ diff_test.go | 30 +++++++++++++++++++++++------- git_test.go | 3 ++- patch.go | 8 ++++---- patch_test.go | 34 ++++++++++++++++++++++++++++++++++ repository.go | 2 +- wrapper.c | 4 +--- 7 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 patch_test.go (limited to 'diff_test.go') diff --git a/diff.go b/diff.go index 31000bc..014cff5 100644 --- a/diff.go +++ b/diff.go @@ -16,7 +16,7 @@ type DiffFlag int const ( DiffFlagBinary DiffFlag = C.GIT_DIFF_FLAG_BINARY DiffFlagNotBinary = C.GIT_DIFF_FLAG_NOT_BINARY - DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_OID + DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_ID ) type Delta int @@ -59,7 +59,7 @@ type DiffFile struct { func newDiffFileFromC(file *C.git_diff_file) *DiffFile { return &DiffFile{ Path: C.GoString(file.path), - Oid: newOidFromC(&file.oid), + Oid: newOidFromC(&file.id), Size: int(file.size), Flags: DiffFlag(file.flags), Mode: uint16(file.mode), @@ -142,7 +142,7 @@ func newDiffFromC(ptr *C.git_diff) *Diff { } func (diff *Diff) Free() error { - if diff.ptr != nil { + if diff.ptr == nil { return ErrInvalid } runtime.SetFinalizer(diff, nil) @@ -150,20 +150,22 @@ func (diff *Diff) Free() error { return nil } +type DiffForEachFileCallback func(*DiffDelta) error + type diffForEachFileData struct { Callback DiffForEachFileCallback Error error } func (diff *Diff) ForEachFile(cb DiffForEachFileCallback) error { - if diff.ptr != nil { + if diff.ptr == nil { return ErrInvalid } data := &diffForEachFileData{ Callback: cb, } - ecode := C._go_git_diff_foreach(diff.ptr, 1, 0, 0, unsafe.Pointer(&data)) + ecode := C._go_git_diff_foreach(diff.ptr, 1, 0, 0, unsafe.Pointer(data)) if ecode < 0 { return data.Error } @@ -172,7 +174,7 @@ func (diff *Diff) ForEachFile(cb DiffForEachFileCallback) error { //export diffForEachFileCb func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe.Pointer) int { - data := *diffForEachFileData(payload) + data := (*diffForEachFileData)(payload) err := data.Callback(newDiffDeltaFromC(delta)) if err != nil { @@ -191,7 +193,7 @@ type diffForEachHunkData struct { type DiffForEachHunkCallback func(*DiffHunk) error func (diff *Diff) ForEachHunk(cb DiffForEachHunkCallback) error { - if diff.ptr != nil { + if diff.ptr == nil { return ErrInvalid } data := &diffForEachHunkData{ @@ -206,10 +208,10 @@ func (diff *Diff) ForEachHunk(cb DiffForEachHunkCallback) error { //export diffForEachHunkCb func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload unsafe.Pointer) int { - data := *diffForEachHunkData(payload) + data := (*diffForEachHunkData)(payload) err := data.Callback(newDiffHunkFromC(delta, hunk)) - if err < 0 { + if err != nil { data.Error = err return -1 } @@ -225,7 +227,7 @@ type diffForEachLineData struct { type DiffForEachLineCallback func(*DiffLine) error func (diff *Diff) ForEachLine(cb DiffForEachLineCallback) error { - if diff.ptr != nil { + if diff.ptr == nil { return ErrInvalid } @@ -243,7 +245,7 @@ func (diff *Diff) ForEachLine(cb DiffForEachLineCallback) error { //export diffForEachLineCb func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, payload unsafe.Pointer) int { - data := *diffForEachLineData(payload) + data := (*diffForEachLineData)(payload) err := data.Callback(newDiffLineFromC(delta, hunk, line)) if err != nil { @@ -255,26 +257,26 @@ func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.g } func (diff *Diff) NumDeltas() (int, error) { - if diff.ptr != nil { + if diff.ptr == nil { return -1, ErrInvalid } return int(C.git_diff_num_deltas(diff.ptr)), nil } func (diff *Diff) GetDelta(index int) (*DiffDelta, error) { - if diff.ptr != nil { + if diff.ptr == nil { return nil, ErrInvalid } ptr := C.git_diff_get_delta(diff.ptr, C.size_t(index)) if ptr == nil { - return nil + return nil, nil } return newDiffDeltaFromC(ptr), nil } func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { - if diff.ptr != nil { + if diff.ptr == nil { return nil, ErrInvalid } var patchPtr *C.git_patch @@ -287,7 +289,7 @@ func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { return newPatchFromC(patchPtr), nil } -func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) *Diff { +func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) (*Diff, error) { var diffPtr *C.git_diff var oldPtr, newPtr *C.git_tree @@ -299,7 +301,10 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) *Diff { newPtr = newTree.gitObject.ptr } - C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, nil) + ecode := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, nil) + if ecode < 0 { + return nil, MakeGitError(ecode) + } - return newDiff(diffPtr) + return newDiffFromC(diffPtr), nil } diff --git a/diff_test.go b/diff_test.go index 15aabc5..6ddd433 100644 --- a/diff_test.go +++ b/diff_test.go @@ -1,41 +1,57 @@ package git import ( + "errors" "testing" ) func TestDiffTreeToTree(t *testing.T) { repo := createTestRepo(t) defer repo.Free() - defer os.RemoveAll(repo.Workdir()) + //defer os.RemoveAll(repo.Workdir()) _, originalTreeId := seedTestRepo(t, repo) originalTree, err := repo.LookupTree(originalTreeId) checkFatal(t, err) - updateReadme(t, repo, "file changed\n") - _, newTreeId := seedTestRepo(t, repo) + _, newTreeId := updateReadme(t, repo, "file changed\n") + newTree, err := repo.LookupTree(newTreeId) checkFatal(t, err) - diff, err := repo.DiffTreeToTree(originalTreeId, newTreeId) + diff, err := repo.DiffTreeToTree(originalTree, newTree) checkFatal(t, err) + if diff == nil { + t.Fatal("no diff returned") + } + files := make([]string, 0) - err := diff.ForEachFile(func(file *DiffFile) error { - files = append(files, file.Path) + err = diff.ForEachFile(func(file *DiffDelta) error { + files = append(files, file.OldFile.Path) return nil }) checkFatal(t, err) - if len(files) != 0 { + if len(files) != 1 { t.Fatal("Incorrect number of files in diff") } if files[0] != "README" { t.Fatal("File in diff was expected to be README") } + + errTest := errors.New("test error") + + err = diff.ForEachFile(func(file *DiffDelta) error { + return errTest + }) + + if err != errTest { + t.Fatal("Expected custom error to be returned") + } + } diff --git a/git_test.go b/git_test.go index e6372fb..6a3aeaa 100644 --- a/git_test.go +++ b/git_test.go @@ -2,6 +2,7 @@ package git import ( "io/ioutil" + "path" "testing" "time" ) @@ -66,7 +67,7 @@ func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) { } tmpfile := "README" - err = ioutil.WriteFile(repo.Path()+"/"+tmpfile, []byte(content), 0644) + err = ioutil.WriteFile(path.Join(path.Dir(path.Dir(repo.Path())), tmpfile), []byte(content), 0644) checkFatal(t, err) idx, err := repo.Index() diff --git a/patch.go b/patch.go index f2016c4..880f088 100644 --- a/patch.go +++ b/patch.go @@ -35,10 +35,10 @@ func (patch *Patch) Free() error { } func (patch *Patch) String() (string, error) { - if diff.ptr != nil { + if patch.ptr == nil { return "", ErrInvalid } - var cptr *C.char - C.git_patch_to_str(&cptr, patch.ptr) - return C.GoString(cptr), nil + var buf C.git_buf + C.git_patch_to_buf(&buf, patch.ptr) + return C.GoString(buf.ptr), nil } diff --git a/patch_test.go b/patch_test.go new file mode 100644 index 0000000..f816068 --- /dev/null +++ b/patch_test.go @@ -0,0 +1,34 @@ +package git + +import ( + "strings" + "testing" +) + +func TestPatch(t *testing.T) { + repo := createTestRepo(t) + defer repo.Free() + //defer os.RemoveAll(repo.Workdir()) + + _, originalTreeId := seedTestRepo(t, repo) + originalTree, err := repo.LookupTree(originalTreeId) + + checkFatal(t, err) + + _, newTreeId := updateReadme(t, repo, "file changed\n") + + newTree, err := repo.LookupTree(newTreeId) + checkFatal(t, err) + + diff, err := repo.DiffTreeToTree(originalTree, newTree) + checkFatal(t, err) + + patch, err := diff.Patch(0) + checkFatal(t, err) + + patchStr, err := patch.String() + checkFatal(t, err) + if strings.Index(patchStr, "diff --git a/README b/README\nindex 257cc56..820734a 100644\n--- a/README\n+++ b/README\n@@ -1 +1 @@\n-foo\n+file changed") == -1 { + t.Fatalf("patch was bad") + } +} diff --git a/repository.go b/repository.go index 50053d9..3d6e59a 100644 --- a/repository.go +++ b/repository.go @@ -154,7 +154,7 @@ func (v *Repository) Head() (*Reference, error) { ecode := C.git_repository_head(&ptr, v.ptr) if ecode < 0 { - return nil, LastError() + return nil, MakeGitError(ecode) } return newReferenceFromC(ptr), nil diff --git a/wrapper.c b/wrapper.c index affa8d6..5d68df5 100644 --- a/wrapper.c +++ b/wrapper.c @@ -25,7 +25,6 @@ int _go_git_odb_foreach(git_odb *db, void *payload) return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCb, payload); } -<<<<<<< HEAD int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload) { git_diff_file_cb fcb = NULL; @@ -45,7 +44,7 @@ int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLin } return git_diff_foreach(diff, fcb, hcb, lcb, payload); -======= +} void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { typedef int (*progress_cb)(const char *str, int len, void *data); typedef int (*completion_cb)(git_remote_completion_type type, void *data); @@ -82,6 +81,5 @@ int _go_git_blob_create_fromchunks(git_oid *id, void *payload) { return git_blob_create_fromchunks(id, repo, hintpath, _go_blob_chunk_cb, payload); ->>>>>>> 2811845a1287d949a74b8ed80a5791fd8875002a } /* EOF */ -- cgit v1.2.3 From f85c38ce22391ef8a932673dabf82219527ab433 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 21 Mar 2014 17:20:48 -0700 Subject: Allow diff.ForEach to enumerate files, hunks, and lines with single call. Support use of closures for enumeration. --- diff.go | 96 +++++++++++++++++++++++++++++++++++------------------------- diff_test.go | 41 +++++++++++++++++++++----- 2 files changed, 90 insertions(+), 47 deletions(-) (limited to 'diff_test.go') diff --git a/diff.go b/diff.go index b8f3780..c34d043 100644 --- a/diff.go +++ b/diff.go @@ -151,22 +151,34 @@ func (diff *Diff) Free() error { return nil } -type DiffForEachFileCallback func(*DiffDelta) error - -type diffForEachFileData struct { - Callback DiffForEachFileCallback - Error error +type diffForEachData struct { + FileCallback DiffForEachFileCallback + HunkCallback DiffForEachHunkCallback + LineCallback DiffForEachLineCallback + Error error } -func (diff *Diff) ForEachFile(cb DiffForEachFileCallback) error { +type DiffForEachFileCallback func(*DiffDelta, float64) (DiffForEachHunkCallback, error) + +func (diff *Diff) ForEach(cbFile DiffForEachFileCallback, diffHunks bool, diffLines bool) error { if diff.ptr == nil { return ErrInvalid } - data := &diffForEachFileData{ - Callback: cb, + intHunks := C.int(0) + if diffHunks { + intHunks = C.int(1) + } + + intLines := C.int(0) + if diffLines { + intLines = C.int(1) + } + + data := &diffForEachData{ + FileCallback: cbFile, } - ecode := C._go_git_diff_foreach(diff.ptr, 1, 0, 0, unsafe.Pointer(data)) + ecode := C._go_git_diff_foreach(diff.ptr, 1, intHunks, intLines, unsafe.Pointer(data)) if ecode < 0 { return data.Error } @@ -175,32 +187,37 @@ func (diff *Diff) ForEachFile(cb DiffForEachFileCallback) error { //export diffForEachFileCb func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe.Pointer) int { - data := (*diffForEachFileData)(payload) - - err := data.Callback(newDiffDeltaFromC(delta)) - if err != nil { - data.Error = err - return -1 + data := (*diffForEachData)(payload) + + data.HunkCallback = nil + if data.FileCallback != nil { + cb, err := data.FileCallback(newDiffDeltaFromC(delta), float64(progress)) + if err != nil { + data.Error = err + return -1 + } + data.HunkCallback = cb } return 0 } -type diffForEachHunkData struct { - Callback DiffForEachHunkCallback - Error error -} +type DiffForEachHunkCallback func(*DiffHunk) (DiffForEachLineCallback, error) -type DiffForEachHunkCallback func(*DiffHunk) error - -func (diff *Diff) ForEachHunk(cb DiffForEachHunkCallback) error { +func (diff *Diff) ForEachHunk(cb DiffForEachHunkCallback, diffLines bool) error { if diff.ptr == nil { return ErrInvalid } - data := &diffForEachHunkData{ - Callback: cb, + data := &diffForEachData{ + HunkCallback: cb, + } + + intLines := C.int(0) + if diffLines { + intLines = C.int(1) } - ecode := C._go_git_diff_foreach(diff.ptr, 0, 1, 0, unsafe.Pointer(data)) + + ecode := C._go_git_diff_foreach(diff.ptr, 0, 1, intLines, unsafe.Pointer(data)) if ecode < 0 { return data.Error } @@ -209,22 +226,21 @@ func (diff *Diff) ForEachHunk(cb DiffForEachHunkCallback) error { //export diffForEachHunkCb func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload unsafe.Pointer) int { - data := (*diffForEachHunkData)(payload) - - err := data.Callback(newDiffHunkFromC(delta, hunk)) - if err != nil { - data.Error = err - return -1 + data := (*diffForEachData)(payload) + + data.LineCallback = nil + if data.HunkCallback != nil { + cb, err := data.HunkCallback(newDiffHunkFromC(delta, hunk)) + if err != nil { + data.Error = err + return -1 + } + data.LineCallback = cb } return 0 } -type diffForEachLineData struct { - Callback DiffForEachLineCallback - Error error -} - type DiffForEachLineCallback func(*DiffLine) error func (diff *Diff) ForEachLine(cb DiffForEachLineCallback) error { @@ -232,8 +248,8 @@ func (diff *Diff) ForEachLine(cb DiffForEachLineCallback) error { return ErrInvalid } - data := &diffForEachLineData{ - Callback: cb, + data := &diffForEachData{ + LineCallback: cb, } ecode := C._go_git_diff_foreach(diff.ptr, 0, 0, 1, unsafe.Pointer(data)) @@ -246,9 +262,9 @@ func (diff *Diff) ForEachLine(cb DiffForEachLineCallback) error { //export diffForEachLineCb func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, payload unsafe.Pointer) int { - data := (*diffForEachLineData)(payload) + data := (*diffForEachData)(payload) - err := data.Callback(newDiffLineFromC(delta, hunk, line)) + err := data.LineCallback(newDiffLineFromC(delta, hunk, line)) if err != nil { data.Error = err return -1 diff --git a/diff_test.go b/diff_test.go index 6ddd433..f3a1ea6 100644 --- a/diff_test.go +++ b/diff_test.go @@ -28,11 +28,18 @@ func TestDiffTreeToTree(t *testing.T) { } files := make([]string, 0) - - err = diff.ForEachFile(func(file *DiffDelta) error { + hunks := make([]*DiffHunk, 0) + lines := make([]*DiffLine, 0) + err = diff.ForEach(func(file *DiffDelta, progress float64) (DiffForEachHunkCallback, error) { files = append(files, file.OldFile.Path) - return nil - }) + return func(hunk *DiffHunk) (DiffForEachLineCallback, error) { + hunks = append(hunks, hunk) + return func(line *DiffLine) error { + lines = append(lines, line) + return nil + }, nil + }, nil + }, true, true) checkFatal(t, err) @@ -44,11 +51,31 @@ func TestDiffTreeToTree(t *testing.T) { t.Fatal("File in diff was expected to be README") } + if len(hunks) != 1 { + t.Fatal("Incorrect number of hunks in diff") + } + + if hunks[0].OldStart != 1 || hunks[0].NewStart != 1 { + t.Fatal("Incorrect hunk") + } + + if len(lines) != 2 { + t.Fatal("Incorrect number of lines in diff") + } + + if lines[0].Content != "foo\n" { + t.Fatal("Incorrect lines in diff") + } + + if lines[1].Content != "file changed\n" { + t.Fatal("Incorrect lines in diff") + } + errTest := errors.New("test error") - err = diff.ForEachFile(func(file *DiffDelta) error { - return errTest - }) + err = diff.ForEach(func(file *DiffDelta, progress float64) (DiffForEachHunkCallback, error) { + return nil, errTest + }, false, false) if err != errTest { t.Fatal("Expected custom error to be returned") -- cgit v1.2.3 From 155f641683f09ec1a9511f42867ae3f278a6800b Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 21 Mar 2014 22:16:26 -0700 Subject: don't expose 3 different diff foreach methods. use structures instead of pointers to structures for diff detail. add patch error code handling. trim excess data from diff structures. --- diff.go | 279 ++++++++++++++++++++++++++++++++++++++++------------------ diff_test.go | 32 ++++--- patch.go | 5 +- patch_test.go | 2 +- push_test.go | 2 - wrapper.c | 5 ++ 6 files changed, 223 insertions(+), 102 deletions(-) (limited to 'diff_test.go') diff --git a/diff.go b/diff.go index c34d043..68ea9af 100644 --- a/diff.go +++ b/diff.go @@ -4,9 +4,11 @@ package git #include 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); */ import "C" import ( + "errors" "runtime" "unsafe" ) @@ -56,8 +58,8 @@ type DiffFile struct { Mode uint16 } -func newDiffFileFromC(file *C.git_diff_file) *DiffFile { - return &DiffFile{ +func diffFileFromC(file *C.git_diff_file) DiffFile { + return DiffFile{ Path: C.GoString(file.path), Oid: newOidFromC(&file.id), Size: int(file.size), @@ -70,17 +72,17 @@ type DiffDelta struct { Status Delta Flags DiffFlag Similarity uint16 - OldFile *DiffFile - NewFile *DiffFile + OldFile DiffFile + NewFile DiffFile } -func newDiffDeltaFromC(delta *C.git_diff_delta) *DiffDelta { - return &DiffDelta{ +func diffDeltaFromC(delta *C.git_diff_delta) DiffDelta { + return DiffDelta{ Status: Delta(delta.status), Flags: DiffFlag(delta.flags), Similarity: uint16(delta.similarity), - OldFile: newDiffFileFromC(&delta.old_file), - NewFile: newDiffFileFromC(&delta.new_file), + OldFile: diffFileFromC(&delta.old_file), + NewFile: diffFileFromC(&delta.new_file), } } @@ -90,17 +92,15 @@ type DiffHunk struct { NewStart int NewLines int Header string - DiffDelta } -func newDiffHunkFromC(delta *C.git_diff_delta, hunk *C.git_diff_hunk) *DiffHunk { - return &DiffHunk{ - OldStart: int(hunk.old_start), - OldLines: int(hunk.old_lines), - NewStart: int(hunk.new_start), - NewLines: int(hunk.new_lines), - Header: C.GoStringN(&hunk.header[0], C.int(hunk.header_len)), - DiffDelta: *newDiffDeltaFromC(delta), +func diffHunkFromC(delta *C.git_diff_delta, hunk *C.git_diff_hunk) DiffHunk { + return DiffHunk{ + OldStart: int(hunk.old_start), + OldLines: int(hunk.old_lines), + NewStart: int(hunk.new_start), + NewLines: int(hunk.new_lines), + Header: C.GoStringN(&hunk.header[0], C.int(hunk.header_len)), } } @@ -110,17 +110,15 @@ type DiffLine struct { NewLineno int NumLines int Content string - DiffHunk } -func newDiffLineFromC(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line) *DiffLine { - return &DiffLine{ +func diffLineFromC(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line) DiffLine { + return DiffLine{ Origin: DiffLineType(line.origin), OldLineno: int(line.old_lineno), NewLineno: int(line.new_lineno), NumLines: int(line.num_lines), Content: C.GoStringN(line.content, C.int(line.content_len)), - DiffHunk: *newDiffHunkFromC(delta, hunk), } } @@ -128,6 +126,21 @@ type Diff struct { ptr *C.git_diff } +func (diff *Diff) NumDeltas() (int, error) { + if diff.ptr == nil { + return -1, ErrInvalid + } + return int(C.git_diff_num_deltas(diff.ptr)), nil +} + +func (diff *Diff) GetDelta(index int) (DiffDelta, error) { + if diff.ptr == nil { + return DiffDelta{}, ErrInvalid + } + ptr := C.git_diff_get_delta(diff.ptr, C.size_t(index)) + return diffDeltaFromC(ptr), nil +} + func newDiffFromC(ptr *C.git_diff) *Diff { if ptr == nil { return nil @@ -158,20 +171,28 @@ type diffForEachData struct { Error error } -type DiffForEachFileCallback func(*DiffDelta, float64) (DiffForEachHunkCallback, error) +type DiffForEachFileCallback func(DiffDelta, float64) (DiffForEachHunkCallback, error) + +type DiffDetail int -func (diff *Diff) ForEach(cbFile DiffForEachFileCallback, diffHunks bool, diffLines bool) error { +const ( + DiffDetailFiles DiffDetail = iota + DiffDetailHunks + DiffDetailLines +) + +func (diff *Diff) ForEach(cbFile DiffForEachFileCallback, detail DiffDetail) error { if diff.ptr == nil { return ErrInvalid } intHunks := C.int(0) - if diffHunks { + if detail >= DiffDetailHunks { intHunks = C.int(1) } intLines := C.int(0) - if diffLines { + if detail >= DiffDetailLines { intLines = C.int(1) } @@ -191,7 +212,7 @@ func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe data.HunkCallback = nil if data.FileCallback != nil { - cb, err := data.FileCallback(newDiffDeltaFromC(delta), float64(progress)) + cb, err := data.FileCallback(diffDeltaFromC(delta), float64(progress)) if err != nil { data.Error = err return -1 @@ -202,27 +223,7 @@ func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe return 0 } -type DiffForEachHunkCallback func(*DiffHunk) (DiffForEachLineCallback, error) - -func (diff *Diff) ForEachHunk(cb DiffForEachHunkCallback, diffLines bool) error { - if diff.ptr == nil { - return ErrInvalid - } - data := &diffForEachData{ - HunkCallback: cb, - } - - intLines := C.int(0) - if diffLines { - intLines = C.int(1) - } - - ecode := C._go_git_diff_foreach(diff.ptr, 0, 1, intLines, unsafe.Pointer(data)) - if ecode < 0 { - return data.Error - } - return nil -} +type DiffForEachHunkCallback func(DiffHunk) (DiffForEachLineCallback, error) //export diffForEachHunkCb func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload unsafe.Pointer) int { @@ -230,7 +231,7 @@ func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload u data.LineCallback = nil if data.HunkCallback != nil { - cb, err := data.HunkCallback(newDiffHunkFromC(delta, hunk)) + cb, err := data.HunkCallback(diffHunkFromC(delta, hunk)) if err != nil { data.Error = err return -1 @@ -241,30 +242,14 @@ func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload u return 0 } -type DiffForEachLineCallback func(*DiffLine) error - -func (diff *Diff) ForEachLine(cb DiffForEachLineCallback) error { - if diff.ptr == nil { - return ErrInvalid - } - - data := &diffForEachData{ - LineCallback: cb, - } - - ecode := C._go_git_diff_foreach(diff.ptr, 0, 0, 1, unsafe.Pointer(data)) - if ecode < 0 { - return data.Error - } - return nil -} +type DiffForEachLineCallback func(DiffLine) error //export diffForEachLineCb func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, payload unsafe.Pointer) int { data := (*diffForEachData)(payload) - err := data.LineCallback(newDiffLineFromC(delta, hunk, line)) + err := data.LineCallback(diffLineFromC(delta, hunk, line)) if err != nil { data.Error = err return -1 @@ -273,25 +258,6 @@ func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.g return 0 } -func (diff *Diff) NumDeltas() (int, error) { - if diff.ptr == nil { - return -1, ErrInvalid - } - return int(C.git_diff_num_deltas(diff.ptr)), nil -} - -func (diff *Diff) GetDelta(index int) (*DiffDelta, error) { - if diff.ptr == nil { - return nil, ErrInvalid - } - ptr := C.git_diff_get_delta(diff.ptr, C.size_t(index)) - if ptr == nil { - return nil, nil - } - - return newDiffDeltaFromC(ptr), nil -} - func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { if diff.ptr == nil { return nil, ErrInvalid @@ -306,7 +272,109 @@ func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { return newPatchFromC(patchPtr), nil } -func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) (*Diff, error) { +type DiffOptionsFlag int + +const ( + DiffNormal DiffOptionsFlag = C.GIT_DIFF_NORMAL + DiffReverse = C.GIT_DIFF_REVERSE + DiffIncludeIgnored = C.GIT_DIFF_INCLUDE_IGNORED + DiffRecurseIgnoredDirs = C.GIT_DIFF_RECURSE_IGNORED_DIRS + DiffIncludeUntracked = C.GIT_DIFF_INCLUDE_UNTRACKED + DiffRecurseUntracked = C.GIT_DIFF_RECURSE_UNTRACKED_DIRS + DiffIncludeUnmodified = C.GIT_DIFF_INCLUDE_UNMODIFIED + DiffIncludeTypeChange = C.GIT_DIFF_INCLUDE_TYPECHANGE + DiffIncludeTypeChangeTrees = C.GIT_DIFF_INCLUDE_TYPECHANGE_TREES + DiffIgnoreFilemode = C.GIT_DIFF_IGNORE_FILEMODE + DiffIgnoreSubmodules = C.GIT_DIFF_IGNORE_SUBMODULES + DiffIgnoreCase = C.GIT_DIFF_IGNORE_CASE + + DiffDisablePathspecMatch = C.GIT_DIFF_DISABLE_PATHSPEC_MATCH + DiffSkipBinaryCheck = C.GIT_DIFF_SKIP_BINARY_CHECK + DiffEnableFastUntrackedDirs = C.GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS + + DiffForceText = C.GIT_DIFF_FORCE_TEXT + DiffForceBinary = C.GIT_DIFF_FORCE_BINARY + + DiffIgnoreWhitespace = C.GIT_DIFF_IGNORE_WHITESPACE + DiffIgnoreWhitespaceChange = C.GIT_DIFF_IGNORE_WHITESPACE_CHANGE + DiffIgnoreWitespaceEol = C.GIT_DIFF_IGNORE_WHITESPACE_EOL + + DiffShowUntrackedContent = C.GIT_DIFF_SHOW_UNTRACKED_CONTENT + DiffShowUnmodified = C.GIT_DIFF_SHOW_UNMODIFIED + DiffPatience = C.GIT_DIFF_PATIENCE + DiffMinimal = C.GIT_DIFF_MINIMAL +) + +type DiffNotifyCallback func(diffSoFar *Diff, deltaToAdd DiffDelta, matchedPathspec string) error + +type DiffOptions struct { + Flags DiffOptionsFlag + IgnoreSubmodules SubmoduleIgnore + Pathspec []string + NotifyCallback DiffNotifyCallback + + ContextLines uint16 + InterhunkLines uint16 + IdAbbrev uint16 + + MaxSize int + + OldPrefix string + NewPrefix string +} + +func DefaultDiffOptions() (DiffOptions, error) { + opts := C.git_diff_options{} + ecode := C.git_diff_init_options(&opts, C.GIT_DIFF_OPTIONS_VERSION) + if ecode < 0 { + return DiffOptions{}, MakeGitError(ecode) + } + + return DiffOptions{ + Flags: DiffOptionsFlag(opts.flags), + IgnoreSubmodules: SubmoduleIgnore(opts.ignore_submodules), + Pathspec: makeStringsFromCStrings(opts.pathspec.strings, int(opts.pathspec.count)), + ContextLines: uint16(opts.context_lines), + InterhunkLines: uint16(opts.interhunk_lines), + IdAbbrev: uint16(opts.id_abbrev), + MaxSize: int(opts.max_size), + }, nil +} + +var ( + ErrDeltaSkip = errors.New("Skip delta") +) + +type diffNotifyData struct { + Callback DiffNotifyCallback + Diff *Diff + Error error +} + +//export diffNotifyCb +func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, matched_pathspec *C.char, payload unsafe.Pointer) int { + diff_so_far := (*C.git_diff)(_diff_so_far) + data := (*diffNotifyData)(payload) + if data != nil { + if data.Diff == nil { + data.Diff = newDiffFromC(diff_so_far) + } + + err := data.Callback(data.Diff, diffDeltaFromC(delta_to_add), C.GoString(matched_pathspec)) + + if err == ErrDeltaSkip { + return 1 + } else if err != nil { + data.Error = err + return -1 + } else { + return 0 + } + } + return 0 +} + +func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (*Diff, error) { var diffPtr *C.git_diff var oldPtr, newPtr *C.git_tree @@ -318,10 +386,45 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) (*Diff, error) { newPtr = newTree.gitObject.ptr } - ecode := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, nil) + cpathspec := C.git_strarray{} + var copts *C.git_diff_options + var notifyData *diffNotifyData + if opts != nil { + notifyData = &diffNotifyData{ + Callback: opts.NotifyCallback, + } + if opts.Pathspec != nil { + cpathspec.count = C.size_t(len(opts.Pathspec)) + cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) + defer freeStrarray(&cpathspec) + } + + copts = &C.git_diff_options{ + version: C.GIT_DIFF_OPTIONS_VERSION, + flags: C.uint32_t(opts.Flags), + ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), + pathspec: cpathspec, + context_lines: C.uint16_t(opts.ContextLines), + interhunk_lines: C.uint16_t(opts.InterhunkLines), + id_abbrev: C.uint16_t(opts.IdAbbrev), + max_size: C.git_off_t(opts.MaxSize), + } + + if opts.NotifyCallback != nil { + C._go_git_setup_diff_notify_callbacks(copts) + copts.notify_payload = unsafe.Pointer(notifyData) + } + } + + ecode := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, copts) if ecode < 0 { return nil, MakeGitError(ecode) } - return newDiffFromC(diffPtr), nil + if notifyData != nil && notifyData.Diff != nil { + return notifyData.Diff, nil + } else { + return newDiffFromC(diffPtr), nil + } + } diff --git a/diff_test.go b/diff_test.go index f3a1ea6..b688294 100644 --- a/diff_test.go +++ b/diff_test.go @@ -2,13 +2,14 @@ package git import ( "errors" + "os" "testing" ) func TestDiffTreeToTree(t *testing.T) { repo := createTestRepo(t) defer repo.Free() - //defer os.RemoveAll(repo.Workdir()) + defer os.RemoveAll(repo.Workdir()) _, originalTreeId := seedTestRepo(t, repo) originalTree, err := repo.LookupTree(originalTreeId) @@ -20,26 +21,37 @@ func TestDiffTreeToTree(t *testing.T) { newTree, err := repo.LookupTree(newTreeId) checkFatal(t, err) - diff, err := repo.DiffTreeToTree(originalTree, newTree) + callbackInvoked := false + opts := DiffOptions{ + NotifyCallback: func(diffSoFar *Diff, delta DiffDelta, matchedPathSpec string) error { + callbackInvoked = true + return nil + }, + } + + diff, err := repo.DiffTreeToTree(originalTree, newTree, &opts) checkFatal(t, err) + if !callbackInvoked { + t.Fatal("callback not invoked") + } if diff == nil { t.Fatal("no diff returned") } files := make([]string, 0) - hunks := make([]*DiffHunk, 0) - lines := make([]*DiffLine, 0) - err = diff.ForEach(func(file *DiffDelta, progress float64) (DiffForEachHunkCallback, error) { + hunks := make([]DiffHunk, 0) + lines := make([]DiffLine, 0) + err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { files = append(files, file.OldFile.Path) - return func(hunk *DiffHunk) (DiffForEachLineCallback, error) { + return func(hunk DiffHunk) (DiffForEachLineCallback, error) { hunks = append(hunks, hunk) - return func(line *DiffLine) error { + return func(line DiffLine) error { lines = append(lines, line) return nil }, nil }, nil - }, true, true) + }, DiffDetailLines) checkFatal(t, err) @@ -73,9 +85,9 @@ func TestDiffTreeToTree(t *testing.T) { errTest := errors.New("test error") - err = diff.ForEach(func(file *DiffDelta, progress float64) (DiffForEachHunkCallback, error) { + err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { return nil, errTest - }, false, false) + }, DiffDetailLines) if err != errTest { t.Fatal("Expected custom error to be returned") diff --git a/patch.go b/patch.go index d927109..0665501 100644 --- a/patch.go +++ b/patch.go @@ -40,6 +40,9 @@ func (patch *Patch) String() (string, error) { return "", ErrInvalid } var buf C.git_buf - C.git_patch_to_buf(&buf, patch.ptr) + ecode := C.git_patch_to_buf(&buf, patch.ptr) + if ecode < 0 { + return "", MakeGitError(ecode) + } return C.GoString(buf.ptr), nil } diff --git a/patch_test.go b/patch_test.go index f816068..569eac2 100644 --- a/patch_test.go +++ b/patch_test.go @@ -20,7 +20,7 @@ func TestPatch(t *testing.T) { newTree, err := repo.LookupTree(newTreeId) checkFatal(t, err) - diff, err := repo.DiffTreeToTree(originalTree, newTree) + diff, err := repo.DiffTreeToTree(originalTree, newTree, nil) checkFatal(t, err) patch, err := diff.Patch(0) diff --git a/push_test.go b/push_test.go index c1e6a22..65f4dd2 100644 --- a/push_test.go +++ b/push_test.go @@ -1,7 +1,6 @@ package git import ( - "log" "os" "testing" "time" @@ -45,7 +44,6 @@ func Test_Push_ToRemote(t *testing.T) { checkFatal(t, err) err = push.StatusForeach(func(ref string, msg string) int { - log.Printf("%s -> %s", ref, msg) return 0 }) checkFatal(t, err) diff --git a/wrapper.c b/wrapper.c index 5d68df5..a8b1432 100644 --- a/wrapper.c +++ b/wrapper.c @@ -45,6 +45,11 @@ int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLin return git_diff_foreach(diff, fcb, hcb, lcb, payload); } + +void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { + opts->notify_cb = (git_diff_notify_cb)diffNotifyCb; +} + void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { typedef int (*progress_cb)(const char *str, int len, void *data); typedef int (*completion_cb)(git_remote_completion_type type, void *data); -- cgit v1.2.3