From bc80beb8432091bdf1190ad6b803060324802685 Mon Sep 17 00:00:00 2001 From: lye Date: Thu, 20 Feb 2014 00:25:30 -0600 Subject: [PATCH 01/13] Add partial diff/patch functionality. This commit adds barebones capacity to generate diffs from two trees and to emit those as git-style diffs (via `Patch.String`), or to enumerate the files/hunks/lines in the diff to emit the data yourself. The walk functions have been implemented in the same manner as the Odb walking methods. Note that not all of the functionality is implemented for either the `git_diff_*` nor the `git_patch_*` functions, and there are unexposed constants which would likely be useful to add. --- diff.go | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++ patch.go | 37 ++++++++ repository.go | 31 +++++++ wrapper.c | 21 +++++ 4 files changed, 318 insertions(+) create mode 100644 diff.go create mode 100644 patch.go diff --git a/diff.go b/diff.go new file mode 100644 index 0000000..d9570c9 --- /dev/null +++ b/diff.go @@ -0,0 +1,229 @@ +package git + +/* +#include + +extern int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload); +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +type DiffFile struct { + file C.git_diff_file +} + +func (df *DiffFile) Oid() *Oid { + return newOidFromC(&df.file.oid) +} + +func (df *DiffFile) Path() string { + return C.GoString(df.file.path) +} + +func (df *DiffFile) Size() int { + return int(df.file.size) +} + +func (df *DiffFile) Flags() uint32 { + return uint32(df.file.flags) +} + +func (df *DiffFile) Mode() uint16 { + return uint16(df.file.mode) +} + +type DiffDelta struct { + delta C.git_diff_delta +} + +func (dd *DiffDelta) Status() int { + return int(dd.delta.status) +} + +func (dd *DiffDelta) Flags() uint32 { + return uint32(dd.delta.flags) +} + +func (dd *DiffDelta) Similarity() uint16 { + return uint16(dd.delta.similarity) +} + +func (dd *DiffDelta) OldFile() *DiffFile { + return &DiffFile{dd.delta.old_file} +} + +func (dd *DiffDelta) NewFile() *DiffFile { + return &DiffFile{dd.delta.new_file} +} + +type DiffHunk struct { + hunk C.git_diff_hunk + DiffDelta +} + +func (dh *DiffHunk) OldStart() int { + return int(dh.hunk.old_start) +} + +func (dh *DiffHunk) OldLines() int { + return int(dh.hunk.old_lines) +} + +func (dh *DiffHunk) NewStart() int { + return int(dh.hunk.new_start) +} + +func (dh *DiffHunk) NewLines() int { + return int(dh.hunk.new_lines) +} + +func (dh *DiffHunk) Header() string { + return C.GoStringN(&dh.hunk.header[0], C.int(dh.hunk.header_len)) +} + +type DiffLine struct { + line C.git_diff_line + DiffHunk +} + +func (dl *DiffLine) Origin() byte { + return byte(dl.line.origin) +} + +func (dl *DiffLine) OldLineno() int { + return int(dl.line.old_lineno) +} + +func (dl *DiffLine) NewLineno() int { + return int(dl.line.new_lineno) +} + +func (dl *DiffLine) NumLines() int { + return int(dl.line.num_lines) +} + +func (dl *DiffLine) Content() string { + return C.GoStringN(dl.line.content, C.int(dl.line.content_len)) +} + +func (dl *DiffLine) ContentOffset() int { + return int(dl.line.content_offset) +} + +type Diff struct { + ptr *C.git_diff +} + +func newDiff(ptr *C.git_diff) *Diff { + if ptr == nil { + return nil + } + + diff := &Diff{ + ptr: ptr, + } + + runtime.SetFinalizer(diff, (*Diff).Free) + return diff +} + +func (diff *Diff) Free() { + runtime.SetFinalizer(diff, nil) + C.git_diff_free(diff.ptr) +} + +func (diff *Diff) forEachFileWrap(ch chan *DiffDelta) { + C._go_git_diff_foreach(diff.ptr, 1, 0, 0, unsafe.Pointer(&ch)) + close(ch) +} + +func (diff *Diff) ForEachFile() chan *DiffDelta { + ch := make(chan *DiffDelta, 0) + go diff.forEachFileWrap(ch) + return ch +} + +//export diffForEachFileCb +func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe.Pointer) int { + ch := *(*chan *DiffDelta)(payload) + + select { + case ch <-&DiffDelta{*delta}: + case <-ch: + return -1 + } + + return 0 +} + +func (diff *Diff) forEachHunkWrap(ch chan *DiffHunk) { + C._go_git_diff_foreach(diff.ptr, 0, 1, 0, unsafe.Pointer(&ch)) + close(ch) +} + +func (diff *Diff) ForEachHunk() chan *DiffHunk { + ch := make(chan *DiffHunk, 0) + go diff.forEachHunkWrap(ch) + return ch +} + +//export diffForEachHunkCb +func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload unsafe.Pointer) int { + ch := *(*chan *DiffHunk)(payload) + + select { + case ch <-&DiffHunk{*hunk, DiffDelta{*delta}}: + case <-ch: + return -1 + } + + return 0 +} + +func (diff *Diff) forEachLineWrap(ch chan *DiffLine) { + C._go_git_diff_foreach(diff.ptr, 0, 0, 1, unsafe.Pointer(&ch)) + close(ch) +} + +func (diff *Diff) ForEachLine() chan *DiffLine { + ch := make(chan *DiffLine, 0) + go diff.forEachLineWrap(ch) + return ch +} + +//export diffForEachLineCb +func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, payload unsafe.Pointer) int { + ch := *(*chan *DiffLine)(payload) + + select { + case ch <-&DiffLine{*line, DiffHunk{*hunk, DiffDelta{*delta}}}: + case <-ch: + return -1 + } + + return 0 +} + +func (diff *Diff) NumDeltas() int { + return int(C.git_diff_num_deltas(diff.ptr)) +} + +func (diff *Diff) GetDelta(index int) *DiffDelta { + ptr := C.git_diff_get_delta(diff.ptr, C.size_t(index)) + if ptr == nil { + return nil + } + + return &DiffDelta{*ptr} +} + +func (diff *Diff) Patch(deltaIndex int) *Patch { + var patchPtr *C.git_patch + + C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex)) + + return newPatch(patchPtr) +} diff --git a/patch.go b/patch.go new file mode 100644 index 0000000..561786e --- /dev/null +++ b/patch.go @@ -0,0 +1,37 @@ +package git + +/* +#include +*/ +import "C" +import ( + "runtime" +) + +type Patch struct { + ptr *C.git_patch +} + +func newPatch(ptr *C.git_patch) *Patch { + if ptr == nil { + return nil + } + + patch := &Patch{ + ptr: ptr, + } + + runtime.SetFinalizer(patch, (*Patch).Free) + return patch +} + +func (patch *Patch) Free() { + runtime.SetFinalizer(patch, nil) + C.git_patch_free(patch.ptr) +} + +func (patch *Patch) String() string { + var cptr *C.char + C.git_patch_to_str(&cptr, patch.ptr) + return C.GoString(cptr) +} diff --git a/repository.go b/repository.go index 3a9068d..58d3487 100644 --- a/repository.go +++ b/repository.go @@ -146,6 +146,20 @@ func (v *Repository) LookupReference(name string) (*Reference, error) { return newReferenceFromC(ptr), nil } +func (v *Repository) Head() (*Reference, error) { + var ptr *C.git_reference + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_repository_head(&ptr, v.ptr) + if ecode < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + func (v *Repository) CreateReference(name string, oid *Oid, force bool) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -241,6 +255,23 @@ func (v *Repository) CreateCommit( return oid, nil } +func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) *Diff { + var diffPtr *C.git_diff + var oldPtr, newPtr *C.git_tree + + if oldTree != nil { + oldPtr = oldTree.gitObject.ptr + } + + if newTree != nil { + newPtr = newTree.gitObject.ptr + } + + C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, nil) + + return newDiff(diffPtr) +} + func (v *Odb) Free() { runtime.SetFinalizer(v, nil) C.git_odb_free(v.ptr) diff --git a/wrapper.c b/wrapper.c index 2af3974..93331ad 100644 --- a/wrapper.c +++ b/wrapper.c @@ -24,4 +24,25 @@ int _go_git_odb_foreach(git_odb *db, void *payload) { return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCb, payload); } + +int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload) +{ + git_diff_file_cb fcb = NULL; + git_diff_hunk_cb hcb = NULL; + git_diff_line_cb lcb = NULL; + + if (eachFile) { + fcb = (git_diff_file_cb)&diffForEachFileCb; + } + + if (eachHunk) { + hcb = (git_diff_hunk_cb)&diffForEachHunkCb; + } + + if (eachLine) { + lcb = (git_diff_line_cb)&diffForEachLineCb; + } + + return git_diff_foreach(diff, fcb, hcb, lcb, payload); +} /* EOF */ From 375168abdc4cfad66d75786e31ee758bc5e374b7 Mon Sep 17 00:00:00 2001 From: lye Date: Sun, 23 Feb 2014 18:04:55 -0600 Subject: [PATCH 02/13] Export the constants and enumerations for diffs. It is possible that the typed enums from libgit2 should be exported as distinct types rather than numeric constants (e.g, `git_delta_t` and `git_line_t` should be typed), but this has not been done in this patch. --- diff.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/diff.go b/diff.go index d9570c9..11493e1 100644 --- a/diff.go +++ b/diff.go @@ -11,6 +11,33 @@ import ( "unsafe" ) +const ( + DiffFlagBinary = C.GIT_DIFF_FLAG_BINARY + DiffFlagNotBinary = C.GIT_DIFF_FLAG_NOT_BINARY + DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_OID + + DeltaUnmodified = C.GIT_DELTA_UNMODIFIED + DeltaAdded = C.GIT_DELTA_ADDED + DeltaDeleted = C.GIT_DELTA_DELETED + DeltaModified = C.GIT_DELTA_MODIFIED + DeltaRenamed = C.GIT_DELTA_RENAMED + DeltaCopied = C.GIT_DELTA_COPIED + DeltaIgnored = C.GIT_DELTA_IGNORED + DeltaUntracked = C.GIT_DELTA_UNTRACKED + DeltaTypeChange = C.GIT_DELTA_TYPECHANGE + + DiffLineContext = C.GIT_DIFF_LINE_CONTEXT + DiffLineAddition = C.GIT_DIFF_LINE_ADDITION + DiffLineDeletion = C.GIT_DIFF_LINE_DELETION + DiffLineContextEOFNL = C.GIT_DIFF_LINE_CONTEXT_EOFNL + DiffLineAddEOFNL = C.GIT_DIFF_LINE_ADD_EOFNL + DiffLineDelEOFNL = C.GIT_DIFF_LINE_DEL_EOFNL + + DiffLineFileHdr = C.GIT_DIFF_LINE_FILE_HDR + DiffLineHunkHdr = C.GIT_DIFF_LINE_HUNK_HDR + DiffLineBinary = C.GIT_DIFF_LINE_BINARY +) + type DiffFile struct { file C.git_diff_file } From 48a02566d63adedb2dc7412996fb7ff5d6d26f72 Mon Sep 17 00:00:00 2001 From: lye Date: Sun, 23 Feb 2014 18:49:04 -0600 Subject: [PATCH 03/13] Pre-emptively copy data when marshalling diff callback data. When marshalling diff callback data to Go structs, any `char*` need to be pre-emptively copied onto Go's heap as they're invalidated as soon as our callback function returns. This patch adds this extra copy before sending the value to the channel, which fixes a bug wherein `DiffLine.Content`, `DiffFile.Path` and `DiffHunk.Header` would previously return garbage data. --- diff.go | 64 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/diff.go b/diff.go index 11493e1..0f3ee0f 100644 --- a/diff.go +++ b/diff.go @@ -40,16 +40,20 @@ const ( type DiffFile struct { file C.git_diff_file + Path string +} + +func newDiffFile(file *C.git_diff_file) *DiffFile { + return &DiffFile{ + file: *file, + Path: C.GoString(file.path), + } } func (df *DiffFile) Oid() *Oid { return newOidFromC(&df.file.oid) } -func (df *DiffFile) Path() string { - return C.GoString(df.file.path) -} - func (df *DiffFile) Size() int { return int(df.file.size) } @@ -64,6 +68,16 @@ func (df *DiffFile) Mode() uint16 { type DiffDelta struct { delta C.git_diff_delta + OldFile *DiffFile + NewFile *DiffFile +} + +func newDiffDelta(delta *C.git_diff_delta) *DiffDelta { + return &DiffDelta{ + delta: *delta, + OldFile: newDiffFile(&delta.old_file), + NewFile: newDiffFile(&delta.new_file), + } } func (dd *DiffDelta) Status() int { @@ -78,19 +92,20 @@ func (dd *DiffDelta) Similarity() uint16 { return uint16(dd.delta.similarity) } -func (dd *DiffDelta) OldFile() *DiffFile { - return &DiffFile{dd.delta.old_file} -} - -func (dd *DiffDelta) NewFile() *DiffFile { - return &DiffFile{dd.delta.new_file} -} - type DiffHunk struct { hunk C.git_diff_hunk + Header string DiffDelta } +func newDiffHunk(delta *C.git_diff_delta, hunk *C.git_diff_hunk) *DiffHunk { + return &DiffHunk{ + hunk: *hunk, + Header: C.GoStringN(&hunk.header[0], C.int(hunk.header_len)), + DiffDelta: *newDiffDelta(delta), + } +} + func (dh *DiffHunk) OldStart() int { return int(dh.hunk.old_start) } @@ -107,15 +122,20 @@ func (dh *DiffHunk) NewLines() int { return int(dh.hunk.new_lines) } -func (dh *DiffHunk) Header() string { - return C.GoStringN(&dh.hunk.header[0], C.int(dh.hunk.header_len)) -} - type DiffLine struct { line C.git_diff_line + Content string DiffHunk } +func newDiffLine(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line) *DiffLine { + return &DiffLine{ + line: *line, + Content: C.GoStringN(line.content, C.int(line.content_len)), + DiffHunk: *newDiffHunk(delta, hunk), + } +} + func (dl *DiffLine) Origin() byte { return byte(dl.line.origin) } @@ -132,10 +152,6 @@ func (dl *DiffLine) NumLines() int { return int(dl.line.num_lines) } -func (dl *DiffLine) Content() string { - return C.GoStringN(dl.line.content, C.int(dl.line.content_len)) -} - func (dl *DiffLine) ContentOffset() int { return int(dl.line.content_offset) } @@ -178,7 +194,7 @@ func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe ch := *(*chan *DiffDelta)(payload) select { - case ch <-&DiffDelta{*delta}: + case ch <-newDiffDelta(delta): case <-ch: return -1 } @@ -202,7 +218,7 @@ func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload u ch := *(*chan *DiffHunk)(payload) select { - case ch <-&DiffHunk{*hunk, DiffDelta{*delta}}: + case ch <-newDiffHunk(delta, hunk): case <-ch: return -1 } @@ -226,7 +242,7 @@ func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.g ch := *(*chan *DiffLine)(payload) select { - case ch <-&DiffLine{*line, DiffHunk{*hunk, DiffDelta{*delta}}}: + case ch <-newDiffLine(delta, hunk, line): case <-ch: return -1 } @@ -244,7 +260,7 @@ func (diff *Diff) GetDelta(index int) *DiffDelta { return nil } - return &DiffDelta{*ptr} + return newDiffDelta(ptr) } func (diff *Diff) Patch(deltaIndex int) *Patch { From 2e5102b71ab412bc2857410caa6f27a4ed2769a3 Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 26 Feb 2014 04:34:38 -0600 Subject: [PATCH 04/13] Unwrap DiffLine; add types for git_diff_flag_t and git_delta_t. --- diff.go | 67 ++++++++++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/diff.go b/diff.go index 0f3ee0f..208a701 100644 --- a/diff.go +++ b/diff.go @@ -11,20 +11,23 @@ import ( "unsafe" ) -const ( - DiffFlagBinary = C.GIT_DIFF_FLAG_BINARY - DiffFlagNotBinary = C.GIT_DIFF_FLAG_NOT_BINARY - DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_OID +type Delta int +type DiffFlag int - DeltaUnmodified = C.GIT_DELTA_UNMODIFIED - DeltaAdded = C.GIT_DELTA_ADDED - DeltaDeleted = C.GIT_DELTA_DELETED - DeltaModified = C.GIT_DELTA_MODIFIED - DeltaRenamed = C.GIT_DELTA_RENAMED - DeltaCopied = C.GIT_DELTA_COPIED - DeltaIgnored = C.GIT_DELTA_IGNORED - DeltaUntracked = C.GIT_DELTA_UNTRACKED - DeltaTypeChange = C.GIT_DELTA_TYPECHANGE +const ( + DiffFlagBinary = DiffFlag(C.GIT_DIFF_FLAG_BINARY) + DiffFlagNotBinary = DiffFlag(C.GIT_DIFF_FLAG_NOT_BINARY) + DiffFlagValidOid = DiffFlag(C.GIT_DIFF_FLAG_VALID_OID) + + DeltaUnmodified = Delta(C.GIT_DELTA_UNMODIFIED) + DeltaAdded = Delta(C.GIT_DELTA_ADDED) + DeltaDeleted = Delta(C.GIT_DELTA_DELETED) + DeltaModified = Delta(C.GIT_DELTA_MODIFIED) + DeltaRenamed = Delta(C.GIT_DELTA_RENAMED) + DeltaCopied = Delta(C.GIT_DELTA_COPIED) + DeltaIgnored = Delta(C.GIT_DELTA_IGNORED) + DeltaUntracked = Delta(C.GIT_DELTA_UNTRACKED) + DeltaTypeChange = Delta(C.GIT_DELTA_TYPECHANGE) DiffLineContext = C.GIT_DIFF_LINE_CONTEXT DiffLineAddition = C.GIT_DIFF_LINE_ADDITION @@ -80,12 +83,12 @@ func newDiffDelta(delta *C.git_diff_delta) *DiffDelta { } } -func (dd *DiffDelta) Status() int { - return int(dd.delta.status) +func (dd *DiffDelta) Status() Delta { + return Delta(dd.delta.status) } -func (dd *DiffDelta) Flags() uint32 { - return uint32(dd.delta.flags) +func (dd *DiffDelta) Flags() DiffFlag { + return DiffFlag(dd.delta.flags) } func (dd *DiffDelta) Similarity() uint16 { @@ -123,39 +126,25 @@ func (dh *DiffHunk) NewLines() int { } type DiffLine struct { - line C.git_diff_line + Origin byte + OldLineno int + NewLineno int + NumLines int Content string DiffHunk } func newDiffLine(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line) *DiffLine { return &DiffLine{ - line: *line, + Origin: byte(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: *newDiffHunk(delta, hunk), } } -func (dl *DiffLine) Origin() byte { - return byte(dl.line.origin) -} - -func (dl *DiffLine) OldLineno() int { - return int(dl.line.old_lineno) -} - -func (dl *DiffLine) NewLineno() int { - return int(dl.line.new_lineno) -} - -func (dl *DiffLine) NumLines() int { - return int(dl.line.num_lines) -} - -func (dl *DiffLine) ContentOffset() int { - return int(dl.line.content_offset) -} - type Diff struct { ptr *C.git_diff } From 45b0f17c04aaefaaa73b792288d4010295d878c6 Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 26 Feb 2014 05:58:45 -0600 Subject: [PATCH 05/13] Bundle consts more idiomatically; add GitLineType alias. --- diff.go | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/diff.go b/diff.go index 208a701..06d2bba 100644 --- a/diff.go +++ b/diff.go @@ -11,24 +11,28 @@ import ( "unsafe" ) -type Delta int type DiffFlag int - const ( - DiffFlagBinary = DiffFlag(C.GIT_DIFF_FLAG_BINARY) - DiffFlagNotBinary = DiffFlag(C.GIT_DIFF_FLAG_NOT_BINARY) - DiffFlagValidOid = DiffFlag(C.GIT_DIFF_FLAG_VALID_OID) + DiffFlagBinary = C.GIT_DIFF_FLAG_BINARY + DiffFlagNotBinary = C.GIT_DIFF_FLAG_NOT_BINARY + DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_OID +) - DeltaUnmodified = Delta(C.GIT_DELTA_UNMODIFIED) - DeltaAdded = Delta(C.GIT_DELTA_ADDED) - DeltaDeleted = Delta(C.GIT_DELTA_DELETED) - DeltaModified = Delta(C.GIT_DELTA_MODIFIED) - DeltaRenamed = Delta(C.GIT_DELTA_RENAMED) - DeltaCopied = Delta(C.GIT_DELTA_COPIED) - DeltaIgnored = Delta(C.GIT_DELTA_IGNORED) - DeltaUntracked = Delta(C.GIT_DELTA_UNTRACKED) - DeltaTypeChange = Delta(C.GIT_DELTA_TYPECHANGE) +type Delta int +const ( + DeltaUnmodified = C.GIT_DELTA_UNMODIFIED + DeltaAdded = C.GIT_DELTA_ADDED + DeltaDeleted = C.GIT_DELTA_DELETED + DeltaModified = C.GIT_DELTA_MODIFIED + DeltaRenamed = C.GIT_DELTA_RENAMED + DeltaCopied = C.GIT_DELTA_COPIED + DeltaIgnored = C.GIT_DELTA_IGNORED + DeltaUntracked = C.GIT_DELTA_UNTRACKED + DeltaTypeChange = C.GIT_DELTA_TYPECHANGE +) +type DiffLineType int +const ( DiffLineContext = C.GIT_DIFF_LINE_CONTEXT DiffLineAddition = C.GIT_DIFF_LINE_ADDITION DiffLineDeletion = C.GIT_DIFF_LINE_DELETION @@ -126,7 +130,7 @@ func (dh *DiffHunk) NewLines() int { } type DiffLine struct { - Origin byte + Origin DiffLineType OldLineno int NewLineno int NumLines int @@ -136,7 +140,7 @@ type DiffLine struct { func newDiffLine(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line) *DiffLine { return &DiffLine{ - Origin: byte(line.origin), + Origin: DiffLineType(line.origin), OldLineno: int(line.old_lineno), NewLineno: int(line.new_lineno), NumLines: int(line.num_lines), From 9acd67e388b6fccf982c9206dc5326ae35c13f49 Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 26 Feb 2014 16:01:32 -0600 Subject: [PATCH 06/13] Actually type constants; unwrap DiffFile, DiffDelta, DiffHunk. --- diff.go | 78 ++++++++++++++++++--------------------------------------- 1 file changed, 25 insertions(+), 53 deletions(-) diff --git a/diff.go b/diff.go index 06d2bba..bf08c8b 100644 --- a/diff.go +++ b/diff.go @@ -13,14 +13,14 @@ import ( type DiffFlag int const ( - DiffFlagBinary = C.GIT_DIFF_FLAG_BINARY + DiffFlagBinary = DiffFlag(C.GIT_DIFF_FLAG_BINARY) DiffFlagNotBinary = C.GIT_DIFF_FLAG_NOT_BINARY DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_OID ) type Delta int const ( - DeltaUnmodified = C.GIT_DELTA_UNMODIFIED + DeltaUnmodified = Delta(C.GIT_DELTA_UNMODIFIED) DeltaAdded = C.GIT_DELTA_ADDED DeltaDeleted = C.GIT_DELTA_DELETED DeltaModified = C.GIT_DELTA_MODIFIED @@ -33,7 +33,7 @@ const ( type DiffLineType int const ( - DiffLineContext = C.GIT_DIFF_LINE_CONTEXT + DiffLineContext = DiffLineType(C.GIT_DIFF_LINE_CONTEXT) DiffLineAddition = C.GIT_DIFF_LINE_ADDITION DiffLineDeletion = C.GIT_DIFF_LINE_DELETION DiffLineContextEOFNL = C.GIT_DIFF_LINE_CONTEXT_EOFNL @@ -46,89 +46,61 @@ const ( ) type DiffFile struct { - file C.git_diff_file Path string + Oid *Oid + Size int + Flags DiffFlag + Mode uint16 } func newDiffFile(file *C.git_diff_file) *DiffFile { return &DiffFile{ - file: *file, Path: C.GoString(file.path), + Oid: newOidFromC(&file.oid), + Size: int(file.size), + Flags: DiffFlag(file.flags), + Mode: uint16(file.mode), } } -func (df *DiffFile) Oid() *Oid { - return newOidFromC(&df.file.oid) -} - -func (df *DiffFile) Size() int { - return int(df.file.size) -} - -func (df *DiffFile) Flags() uint32 { - return uint32(df.file.flags) -} - -func (df *DiffFile) Mode() uint16 { - return uint16(df.file.mode) -} - type DiffDelta struct { - delta C.git_diff_delta + Status Delta + Flags DiffFlag + Similarity uint16 OldFile *DiffFile NewFile *DiffFile } func newDiffDelta(delta *C.git_diff_delta) *DiffDelta { return &DiffDelta{ - delta: *delta, + Status: Delta(delta.status), + Flags: DiffFlag(delta.flags), + Similarity: uint16(delta.similarity), OldFile: newDiffFile(&delta.old_file), NewFile: newDiffFile(&delta.new_file), } } -func (dd *DiffDelta) Status() Delta { - return Delta(dd.delta.status) -} - -func (dd *DiffDelta) Flags() DiffFlag { - return DiffFlag(dd.delta.flags) -} - -func (dd *DiffDelta) Similarity() uint16 { - return uint16(dd.delta.similarity) -} - type DiffHunk struct { - hunk C.git_diff_hunk + OldStart int + OldLines int + NewStart int + NewLines int Header string DiffDelta } func newDiffHunk(delta *C.git_diff_delta, hunk *C.git_diff_hunk) *DiffHunk { return &DiffHunk{ - hunk: *hunk, + 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: *newDiffDelta(delta), } } -func (dh *DiffHunk) OldStart() int { - return int(dh.hunk.old_start) -} - -func (dh *DiffHunk) OldLines() int { - return int(dh.hunk.old_lines) -} - -func (dh *DiffHunk) NewStart() int { - return int(dh.hunk.new_start) -} - -func (dh *DiffHunk) NewLines() int { - return int(dh.hunk.new_lines) -} - type DiffLine struct { Origin DiffLineType OldLineno int From d0b334b24409ddc190a7010be0072d87df6b6bfe Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 20 Mar 2014 21:56:41 -0700 Subject: [PATCH 07/13] cleanup and refactor diff / patch --- diff.go | 258 +++++++++++++++++++++++++++++++------------------- diff_test.go | 41 ++++++++ git.go | 13 +-- git_test.go | 32 ++++++- patch.go | 15 ++- repository.go | 17 ---- 6 files changed, 252 insertions(+), 124 deletions(-) create mode 100644 diff_test.go diff --git a/diff.go b/diff.go index bf08c8b..31000bc 100644 --- a/diff.go +++ b/diff.go @@ -12,72 +12,75 @@ import ( ) 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 + DiffFlagBinary DiffFlag = C.GIT_DIFF_FLAG_BINARY + DiffFlagNotBinary = C.GIT_DIFF_FLAG_NOT_BINARY + DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_OID ) type Delta int + const ( - DeltaUnmodified = Delta(C.GIT_DELTA_UNMODIFIED) - DeltaAdded = C.GIT_DELTA_ADDED - DeltaDeleted = C.GIT_DELTA_DELETED - DeltaModified = C.GIT_DELTA_MODIFIED - DeltaRenamed = C.GIT_DELTA_RENAMED - DeltaCopied = C.GIT_DELTA_COPIED - DeltaIgnored = C.GIT_DELTA_IGNORED - DeltaUntracked = C.GIT_DELTA_UNTRACKED - DeltaTypeChange = C.GIT_DELTA_TYPECHANGE + DeltaUnmodified Delta = C.GIT_DELTA_UNMODIFIED + DeltaAdded = C.GIT_DELTA_ADDED + DeltaDeleted = C.GIT_DELTA_DELETED + DeltaModified = C.GIT_DELTA_MODIFIED + DeltaRenamed = C.GIT_DELTA_RENAMED + DeltaCopied = C.GIT_DELTA_COPIED + DeltaIgnored = C.GIT_DELTA_IGNORED + DeltaUntracked = C.GIT_DELTA_UNTRACKED + DeltaTypeChange = C.GIT_DELTA_TYPECHANGE ) type DiffLineType int + const ( - DiffLineContext = DiffLineType(C.GIT_DIFF_LINE_CONTEXT) - DiffLineAddition = C.GIT_DIFF_LINE_ADDITION - DiffLineDeletion = C.GIT_DIFF_LINE_DELETION - DiffLineContextEOFNL = C.GIT_DIFF_LINE_CONTEXT_EOFNL - DiffLineAddEOFNL = C.GIT_DIFF_LINE_ADD_EOFNL - DiffLineDelEOFNL = C.GIT_DIFF_LINE_DEL_EOFNL + DiffLineContext DiffLineType = C.GIT_DIFF_LINE_CONTEXT + DiffLineAddition = C.GIT_DIFF_LINE_ADDITION + DiffLineDeletion = C.GIT_DIFF_LINE_DELETION + DiffLineContextEOFNL = C.GIT_DIFF_LINE_CONTEXT_EOFNL + DiffLineAddEOFNL = C.GIT_DIFF_LINE_ADD_EOFNL + DiffLineDelEOFNL = C.GIT_DIFF_LINE_DEL_EOFNL DiffLineFileHdr = C.GIT_DIFF_LINE_FILE_HDR DiffLineHunkHdr = C.GIT_DIFF_LINE_HUNK_HDR - DiffLineBinary = C.GIT_DIFF_LINE_BINARY + DiffLineBinary = C.GIT_DIFF_LINE_BINARY ) type DiffFile struct { - Path string - Oid *Oid - Size int + Path string + Oid *Oid + Size int Flags DiffFlag - Mode uint16 + Mode uint16 } -func newDiffFile(file *C.git_diff_file) *DiffFile { +func newDiffFileFromC(file *C.git_diff_file) *DiffFile { return &DiffFile{ - Path: C.GoString(file.path), - Oid: newOidFromC(&file.oid), - Size: int(file.size), + Path: C.GoString(file.path), + Oid: newOidFromC(&file.oid), + Size: int(file.size), Flags: DiffFlag(file.flags), - Mode: uint16(file.mode), + Mode: uint16(file.mode), } } type DiffDelta struct { - Status Delta - Flags DiffFlag + Status Delta + Flags DiffFlag Similarity uint16 - OldFile *DiffFile - NewFile *DiffFile + OldFile *DiffFile + NewFile *DiffFile } -func newDiffDelta(delta *C.git_diff_delta) *DiffDelta { +func newDiffDeltaFromC(delta *C.git_diff_delta) *DiffDelta { return &DiffDelta{ - Status: Delta(delta.status), - Flags: DiffFlag(delta.flags), + Status: Delta(delta.status), + Flags: DiffFlag(delta.flags), Similarity: uint16(delta.similarity), - OldFile: newDiffFile(&delta.old_file), - NewFile: newDiffFile(&delta.new_file), + OldFile: newDiffFileFromC(&delta.old_file), + NewFile: newDiffFileFromC(&delta.new_file), } } @@ -86,38 +89,38 @@ type DiffHunk struct { OldLines int NewStart int NewLines int - Header string + Header string DiffDelta } -func newDiffHunk(delta *C.git_diff_delta, hunk *C.git_diff_hunk) *DiffHunk { +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: *newDiffDelta(delta), + 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), } } type DiffLine struct { - Origin DiffLineType + Origin DiffLineType OldLineno int NewLineno int - NumLines int - Content string + NumLines int + Content string DiffHunk } -func newDiffLine(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line) *DiffLine { +func newDiffLineFromC(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line) *DiffLine { return &DiffLine{ - Origin: DiffLineType(line.origin), + 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: *newDiffHunk(delta, hunk), + NumLines: int(line.num_lines), + Content: C.GoStringN(line.content, C.int(line.content_len)), + DiffHunk: *newDiffHunkFromC(delta, hunk), } } @@ -125,7 +128,7 @@ type Diff struct { ptr *C.git_diff } -func newDiff(ptr *C.git_diff) *Diff { +func newDiffFromC(ptr *C.git_diff) *Diff { if ptr == nil { return nil } @@ -138,100 +141,165 @@ func newDiff(ptr *C.git_diff) *Diff { return diff } -func (diff *Diff) Free() { +func (diff *Diff) Free() error { + if diff.ptr != nil { + return ErrInvalid + } runtime.SetFinalizer(diff, nil) C.git_diff_free(diff.ptr) + return nil } -func (diff *Diff) forEachFileWrap(ch chan *DiffDelta) { - C._go_git_diff_foreach(diff.ptr, 1, 0, 0, unsafe.Pointer(&ch)) - close(ch) +type diffForEachFileData struct { + Callback DiffForEachFileCallback + Error error } -func (diff *Diff) ForEachFile() chan *DiffDelta { - ch := make(chan *DiffDelta, 0) - go diff.forEachFileWrap(ch) - return ch +func (diff *Diff) ForEachFile(cb DiffForEachFileCallback) error { + if diff.ptr != nil { + return ErrInvalid + } + + data := &diffForEachFileData{ + Callback: cb, + } + ecode := C._go_git_diff_foreach(diff.ptr, 1, 0, 0, unsafe.Pointer(&data)) + if ecode < 0 { + return data.Error + } + return nil } //export diffForEachFileCb func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe.Pointer) int { - ch := *(*chan *DiffDelta)(payload) + data := *diffForEachFileData(payload) - select { - case ch <-newDiffDelta(delta): - case <-ch: + err := data.Callback(newDiffDeltaFromC(delta)) + if err != nil { + data.Error = err return -1 } return 0 } -func (diff *Diff) forEachHunkWrap(ch chan *DiffHunk) { - C._go_git_diff_foreach(diff.ptr, 0, 1, 0, unsafe.Pointer(&ch)) - close(ch) +type diffForEachHunkData struct { + Callback DiffForEachHunkCallback + Error error } -func (diff *Diff) ForEachHunk() chan *DiffHunk { - ch := make(chan *DiffHunk, 0) - go diff.forEachHunkWrap(ch) - return ch +type DiffForEachHunkCallback func(*DiffHunk) error + +func (diff *Diff) ForEachHunk(cb DiffForEachHunkCallback) error { + if diff.ptr != nil { + return ErrInvalid + } + data := &diffForEachHunkData{ + Callback: cb, + } + ecode := C._go_git_diff_foreach(diff.ptr, 0, 1, 0, unsafe.Pointer(data)) + if ecode < 0 { + return data.Error + } + return nil } //export diffForEachHunkCb func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload unsafe.Pointer) int { - ch := *(*chan *DiffHunk)(payload) + data := *diffForEachHunkData(payload) - select { - case ch <-newDiffHunk(delta, hunk): - case <-ch: + err := data.Callback(newDiffHunkFromC(delta, hunk)) + if err < 0 { + data.Error = err return -1 } return 0 } -func (diff *Diff) forEachLineWrap(ch chan *DiffLine) { - C._go_git_diff_foreach(diff.ptr, 0, 0, 1, unsafe.Pointer(&ch)) - close(ch) +type diffForEachLineData struct { + Callback DiffForEachLineCallback + Error error } -func (diff *Diff) ForEachLine() chan *DiffLine { - ch := make(chan *DiffLine, 0) - go diff.forEachLineWrap(ch) - return ch +type DiffForEachLineCallback func(*DiffLine) error + +func (diff *Diff) ForEachLine(cb DiffForEachLineCallback) error { + if diff.ptr != nil { + return ErrInvalid + } + + data := &diffForEachLineData{ + Callback: cb, + } + + ecode := C._go_git_diff_foreach(diff.ptr, 0, 0, 1, unsafe.Pointer(data)) + if ecode < 0 { + return data.Error + } + return nil } //export diffForEachLineCb func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, payload unsafe.Pointer) int { - ch := *(*chan *DiffLine)(payload) - select { - case ch <-newDiffLine(delta, hunk, line): - case <-ch: + data := *diffForEachLineData(payload) + + err := data.Callback(newDiffLineFromC(delta, hunk, line)) + if err != nil { + data.Error = err return -1 } return 0 } -func (diff *Diff) NumDeltas() int { - return int(C.git_diff_num_deltas(diff.ptr)) +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 { +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 } - return newDiffDelta(ptr) + return newDiffDeltaFromC(ptr), nil } -func (diff *Diff) Patch(deltaIndex int) *Patch { +func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { + if diff.ptr != nil { + return nil, ErrInvalid + } var patchPtr *C.git_patch - C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex)) + ecode := C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex)) + if ecode < 0 { + return nil, MakeGitError(ecode) + } - return newPatch(patchPtr) + return newPatchFromC(patchPtr), nil +} + +func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) *Diff { + var diffPtr *C.git_diff + var oldPtr, newPtr *C.git_tree + + if oldTree != nil { + oldPtr = oldTree.gitObject.ptr + } + + if newTree != nil { + newPtr = newTree.gitObject.ptr + } + + C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, nil) + + return newDiff(diffPtr) } 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") + } +} diff --git a/git.go b/git.go index 28196c8..4c798b3 100644 --- a/git.go +++ b/git.go @@ -10,8 +10,8 @@ import ( "bytes" "errors" "runtime" - "unsafe" "strings" + "unsafe" ) const ( @@ -22,6 +22,7 @@ const ( var ( ErrIterOver = errors.New("Iteration is over") + ErrInvalid = errors.New("Invalid state for operation") ) func init() { @@ -93,7 +94,7 @@ func (oid *Oid) Equal(oid2 *Oid) bool { } func (oid *Oid) IsZero() bool { - for _, a := range(oid.bytes) { + for _, a := range oid.bytes { if a != '0' { return false } @@ -131,10 +132,10 @@ func ShortenOids(ids []*Oid, minlen int) (int, error) { type GitError struct { Message string - Code int + Code int } -func (e GitError) Error() string{ +func (e GitError) Error() string { return e.Message } @@ -147,14 +148,14 @@ func LastError() error { } func cbool(b bool) C.int { - if (b) { + if b { return C.int(1) } return C.int(0) } func ucbool(b bool) C.uint { - if (b) { + if b { return C.uint(1) } return C.uint(0) diff --git a/git_test.go b/git_test.go index 52aea1d..2f586b5 100644 --- a/git_test.go +++ b/git_test.go @@ -1,8 +1,8 @@ package git import ( - "testing" "io/ioutil" + "testing" "time" ) @@ -14,7 +14,7 @@ func createTestRepo(t *testing.T) *Repository { checkFatal(t, err) tmpfile := "README" - err = ioutil.WriteFile(path + "/" + tmpfile, []byte("foo\n"), 0644) + err = ioutil.WriteFile(path+"/"+tmpfile, []byte("foo\n"), 0644) checkFatal(t, err) return repo @@ -45,3 +45,31 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) { return commitId, treeId } +func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) { + loc, err := time.LoadLocation("Europe/Berlin") + checkFatal(t, err) + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "random@hacker.com", + When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), + } + + tmpfile := "README" + err = ioutil.WriteFile(repo.Path()+"/"+tmpfile, []byte(content), 0644) + checkFatal(t, err) + + idx, err := repo.Index() + checkFatal(t, err) + err = idx.AddByPath("README") + checkFatal(t, err) + treeId, err := idx.WriteTree() + checkFatal(t, err) + + message := "This is a commit\n" + tree, err := repo.LookupTree(treeId) + checkFatal(t, err) + commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) + checkFatal(t, err) + + return commitId, treeId +} diff --git a/patch.go b/patch.go index 561786e..f2016c4 100644 --- a/patch.go +++ b/patch.go @@ -12,7 +12,7 @@ type Patch struct { ptr *C.git_patch } -func newPatch(ptr *C.git_patch) *Patch { +func newPatchFromC(ptr *C.git_patch) *Patch { if ptr == nil { return nil } @@ -25,13 +25,20 @@ func newPatch(ptr *C.git_patch) *Patch { return patch } -func (patch *Patch) Free() { +func (patch *Patch) Free() error { + if patch.ptr == nil { + return ErrInvalid + } runtime.SetFinalizer(patch, nil) C.git_patch_free(patch.ptr) + return nil } -func (patch *Patch) String() string { +func (patch *Patch) String() (string, error) { + if diff.ptr != nil { + return "", ErrInvalid + } var cptr *C.char C.git_patch_to_str(&cptr, patch.ptr) - return C.GoString(cptr) + return C.GoString(cptr), nil } diff --git a/repository.go b/repository.go index 58d3487..8bb061b 100644 --- a/repository.go +++ b/repository.go @@ -255,23 +255,6 @@ func (v *Repository) CreateCommit( return oid, nil } -func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree) *Diff { - var diffPtr *C.git_diff - var oldPtr, newPtr *C.git_tree - - if oldTree != nil { - oldPtr = oldTree.gitObject.ptr - } - - if newTree != nil { - newPtr = newTree.gitObject.ptr - } - - C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, nil) - - return newDiff(diffPtr) -} - func (v *Odb) Free() { runtime.SetFinalizer(v, nil) C.git_odb_free(v.ptr) From d78036fe24c9c746d3513f9e97f463f995aef0fd Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 20 Mar 2014 22:54:18 -0700 Subject: [PATCH 08/13] 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 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 */ From aea899e877cd9567fad5a8be7cc2cfb6d8fbb00f Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 20 Mar 2014 23:19:22 -0700 Subject: [PATCH 09/13] set ptr to nil after free --- diff.go | 1 + patch.go | 1 + 2 files changed, 2 insertions(+) diff --git a/diff.go b/diff.go index 014cff5..b8f3780 100644 --- a/diff.go +++ b/diff.go @@ -147,6 +147,7 @@ func (diff *Diff) Free() error { } runtime.SetFinalizer(diff, nil) C.git_diff_free(diff.ptr) + diff.ptr = nil return nil } diff --git a/patch.go b/patch.go index 880f088..d927109 100644 --- a/patch.go +++ b/patch.go @@ -31,6 +31,7 @@ func (patch *Patch) Free() error { } runtime.SetFinalizer(patch, nil) C.git_patch_free(patch.ptr) + patch.ptr = nil return nil } From f85c38ce22391ef8a932673dabf82219527ab433 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 21 Mar 2014 17:20:48 -0700 Subject: [PATCH 10/13] Allow diff.ForEach to enumerate files, hunks, and lines with single call. Support use of closures for enumeration. --- diff.go | 92 ++++++++++++++++++++++++++++++---------------------- diff_test.go | 41 +++++++++++++++++++---- 2 files changed, 88 insertions(+), 45 deletions(-) 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) } - ecode := C._go_git_diff_foreach(diff.ptr, 1, 0, 0, unsafe.Pointer(data)) + + intLines := C.int(0) + if diffLines { + intLines = C.int(1) + } + + data := &diffForEachData{ + FileCallback: cbFile, + } + 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) + data := (*diffForEachData)(payload) - err := data.Callback(newDiffDeltaFromC(delta)) - if err != nil { - data.Error = err - return -1 + 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, } - ecode := C._go_git_diff_foreach(diff.ptr, 0, 1, 0, unsafe.Pointer(data)) + + 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 } @@ -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) + data := (*diffForEachData)(payload) - err := data.Callback(newDiffHunkFromC(delta, hunk)) - if err != nil { - data.Error = err - return -1 + 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") From 155f641683f09ec1a9511f42867ae3f278a6800b Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 21 Mar 2014 22:16:26 -0700 Subject: [PATCH 11/13] 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(-) 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) -func (diff *Diff) ForEach(cbFile DiffForEachFileCallback, diffHunks bool, diffLines bool) error { +type DiffDetail int + +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); From 4553b3ada6be15c47250bfb7948493b6cb88031b Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Wed, 26 Mar 2014 12:20:54 -0700 Subject: [PATCH 12/13] fix go 1.0 compile error --- diff.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/diff.go b/diff.go index 68ea9af..09481b4 100644 --- a/diff.go +++ b/diff.go @@ -423,8 +423,6 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) ( if notifyData != nil && notifyData.Diff != nil { return notifyData.Diff, nil - } else { - return newDiffFromC(diffPtr), nil } - + return newDiffFromC(diffPtr), nil } From 63fd1f9b032c92b330948cd66dfa0b677d982d03 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 4 Apr 2014 09:42:17 -0700 Subject: [PATCH 13/13] use cast_ptr instead of ptr for travis go tip build --- diff.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diff.go b/diff.go index 09481b4..6f3f554 100644 --- a/diff.go +++ b/diff.go @@ -379,11 +379,11 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) ( var oldPtr, newPtr *C.git_tree if oldTree != nil { - oldPtr = oldTree.gitObject.ptr + oldPtr = oldTree.cast_ptr } if newTree != nil { - newPtr = newTree.gitObject.ptr + newPtr = newTree.cast_ptr } cpathspec := C.git_strarray{}