diff options
| -rw-r--r-- | diff.go | 305 | ||||
| -rw-r--r-- | diff_test.go | 41 | ||||
| -rw-r--r-- | git.go | 1 | ||||
| -rw-r--r-- | git_test.go | 30 | ||||
| -rw-r--r-- | patch.go | 44 | ||||
| -rw-r--r-- | repository.go | 14 | ||||
| -rw-r--r-- | wrapper.c | 22 |
7 files changed, 457 insertions, 0 deletions
@@ -0,0 +1,305 @@ +package git + +/* +#include <git2.h> + +extern int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload); +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +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 +) + +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 +) + +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 + + DiffLineFileHdr = C.GIT_DIFF_LINE_FILE_HDR + DiffLineHunkHdr = C.GIT_DIFF_LINE_HUNK_HDR + DiffLineBinary = C.GIT_DIFF_LINE_BINARY +) + +type DiffFile struct { + Path string + Oid *Oid + Size int + Flags DiffFlag + Mode uint16 +} + +func newDiffFileFromC(file *C.git_diff_file) *DiffFile { + return &DiffFile{ + Path: C.GoString(file.path), + Oid: newOidFromC(&file.oid), + Size: int(file.size), + Flags: DiffFlag(file.flags), + Mode: uint16(file.mode), + } +} + +type DiffDelta struct { + Status Delta + Flags DiffFlag + Similarity uint16 + OldFile *DiffFile + NewFile *DiffFile +} + +func newDiffDeltaFromC(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), + } +} + +type DiffHunk struct { + OldStart int + OldLines int + 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), + } +} + +type DiffLine struct { + Origin DiffLineType + OldLineno int + 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{ + 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), + } +} + +type Diff struct { + ptr *C.git_diff +} + +func newDiffFromC(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() error { + if diff.ptr != nil { + return ErrInvalid + } + runtime.SetFinalizer(diff, nil) + C.git_diff_free(diff.ptr) + return nil +} + +type diffForEachFileData struct { + Callback DiffForEachFileCallback + Error error +} + +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 { + data := *diffForEachFileData(payload) + + err := data.Callback(newDiffDeltaFromC(delta)) + if err != nil { + data.Error = err + return -1 + } + + return 0 +} + +type diffForEachHunkData struct { + Callback DiffForEachHunkCallback + Error error +} + +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 { + data := *diffForEachHunkData(payload) + + err := data.Callback(newDiffHunkFromC(delta, hunk)) + if err < 0 { + data.Error = err + return -1 + } + + return 0 +} + +type diffForEachLineData struct { + Callback DiffForEachLineCallback + Error error +} + +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 { + + 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, 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 + } + + return newDiffDeltaFromC(ptr), nil +} + +func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { + if diff.ptr != nil { + return nil, ErrInvalid + } + var patchPtr *C.git_patch + + ecode := C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex)) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + + 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") + } +} @@ -23,6 +23,7 @@ const ( var ( ErrIterOver = errors.New("Iteration is over") + ErrInvalid = errors.New("Invalid state for operation") ) func init() { diff --git a/git_test.go b/git_test.go index 6542ca0..e6372fb 100644 --- a/git_test.go +++ b/git_test.go @@ -15,6 +15,7 @@ func createTestRepo(t *testing.T) *Repository { tmpfile := "README" err = ioutil.WriteFile(path+"/"+tmpfile, []byte("foo\n"), 0644) + checkFatal(t, err) return repo @@ -55,6 +56,35 @@ 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: "[email protected]", + 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 +} + func TestOidZero(t *testing.T) { var zeroId Oid diff --git a/patch.go b/patch.go new file mode 100644 index 0000000..f2016c4 --- /dev/null +++ b/patch.go @@ -0,0 +1,44 @@ +package git + +/* +#include <git2.h> +*/ +import "C" +import ( + "runtime" +) + +type Patch struct { + ptr *C.git_patch +} + +func newPatchFromC(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() error { + if patch.ptr == nil { + return ErrInvalid + } + runtime.SetFinalizer(patch, nil) + C.git_patch_free(patch.ptr) + return nil +} + +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), nil +} diff --git a/repository.go b/repository.go index d6eadc8..50053d9 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, id *Oid, force bool, sig *Signature, msg string) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -25,6 +25,27 @@ 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; + 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); +======= 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); @@ -61,5 +82,6 @@ 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 */ |
