Merge commit 'refs/pull/72/head' of github.com:libgit2/git2go
Conflicts: git.go wrapper.c
This commit is contained in:
commit
5809f03108
|
@ -0,0 +1,428 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiffFlag int
|
||||||
|
|
||||||
|
const (
|
||||||
|
DiffFlagBinary DiffFlag = C.GIT_DIFF_FLAG_BINARY
|
||||||
|
DiffFlagNotBinary = C.GIT_DIFF_FLAG_NOT_BINARY
|
||||||
|
DiffFlagValidOid = C.GIT_DIFF_FLAG_VALID_ID
|
||||||
|
)
|
||||||
|
|
||||||
|
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 diffFileFromC(file *C.git_diff_file) DiffFile {
|
||||||
|
return DiffFile{
|
||||||
|
Path: C.GoString(file.path),
|
||||||
|
Oid: newOidFromC(&file.id),
|
||||||
|
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 diffDeltaFromC(delta *C.git_diff_delta) DiffDelta {
|
||||||
|
return DiffDelta{
|
||||||
|
Status: Delta(delta.status),
|
||||||
|
Flags: DiffFlag(delta.flags),
|
||||||
|
Similarity: uint16(delta.similarity),
|
||||||
|
OldFile: diffFileFromC(&delta.old_file),
|
||||||
|
NewFile: diffFileFromC(&delta.new_file),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffHunk struct {
|
||||||
|
OldStart int
|
||||||
|
OldLines int
|
||||||
|
NewStart int
|
||||||
|
NewLines int
|
||||||
|
Header string
|
||||||
|
}
|
||||||
|
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffLine struct {
|
||||||
|
Origin DiffLineType
|
||||||
|
OldLineno int
|
||||||
|
NewLineno int
|
||||||
|
NumLines int
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
diff.ptr = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type diffForEachData struct {
|
||||||
|
FileCallback DiffForEachFileCallback
|
||||||
|
HunkCallback DiffForEachHunkCallback
|
||||||
|
LineCallback DiffForEachLineCallback
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffForEachFileCallback func(DiffDelta, float64) (DiffForEachHunkCallback, 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 detail >= DiffDetailHunks {
|
||||||
|
intHunks = C.int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
intLines := C.int(0)
|
||||||
|
if detail >= DiffDetailLines {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//export diffForEachFileCb
|
||||||
|
func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe.Pointer) int {
|
||||||
|
data := (*diffForEachData)(payload)
|
||||||
|
|
||||||
|
data.HunkCallback = nil
|
||||||
|
if data.FileCallback != nil {
|
||||||
|
cb, err := data.FileCallback(diffDeltaFromC(delta), float64(progress))
|
||||||
|
if err != nil {
|
||||||
|
data.Error = err
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
data.HunkCallback = cb
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffForEachHunkCallback func(DiffHunk) (DiffForEachLineCallback, error)
|
||||||
|
|
||||||
|
//export diffForEachHunkCb
|
||||||
|
func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload unsafe.Pointer) int {
|
||||||
|
data := (*diffForEachData)(payload)
|
||||||
|
|
||||||
|
data.LineCallback = nil
|
||||||
|
if data.HunkCallback != nil {
|
||||||
|
cb, err := data.HunkCallback(diffHunkFromC(delta, hunk))
|
||||||
|
if err != nil {
|
||||||
|
data.Error = err
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
data.LineCallback = cb
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
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(diffLineFromC(delta, hunk, line))
|
||||||
|
if err != nil {
|
||||||
|
data.Error = err
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if oldTree != nil {
|
||||||
|
oldPtr = oldTree.cast_ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
if newTree != nil {
|
||||||
|
newPtr = newTree.cast_ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if notifyData != nil && notifyData.Diff != nil {
|
||||||
|
return notifyData.Diff, nil
|
||||||
|
}
|
||||||
|
return newDiffFromC(diffPtr), nil
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"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)
|
||||||
|
|
||||||
|
_, newTreeId := updateReadme(t, repo, "file changed\n")
|
||||||
|
|
||||||
|
newTree, err := repo.LookupTree(newTreeId)
|
||||||
|
checkFatal(t, err)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
files = append(files, file.OldFile.Path)
|
||||||
|
return func(hunk DiffHunk) (DiffForEachLineCallback, error) {
|
||||||
|
hunks = append(hunks, hunk)
|
||||||
|
return func(line DiffLine) error {
|
||||||
|
lines = append(lines, line)
|
||||||
|
return nil
|
||||||
|
}, nil
|
||||||
|
}, nil
|
||||||
|
}, DiffDetailLines)
|
||||||
|
|
||||||
|
checkFatal(t, err)
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) {
|
||||||
|
return nil, errTest
|
||||||
|
}, DiffDetailLines)
|
||||||
|
|
||||||
|
if err != errTest {
|
||||||
|
t.Fatal("Expected custom error to be returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
git.go
4
git.go
|
@ -89,6 +89,10 @@ const (
|
||||||
ErrIterOver = C.GIT_ITEROVER
|
ErrIterOver = C.GIT_ITEROVER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalid = errors.New("Invalid state for operation")
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
C.git_threads_init()
|
C.git_threads_init()
|
||||||
}
|
}
|
||||||
|
|
31
git_test.go
31
git_test.go
|
@ -2,6 +2,7 @@ package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -15,6 +16,7 @@ func createTestRepo(t *testing.T) *Repository {
|
||||||
|
|
||||||
tmpfile := "README"
|
tmpfile := "README"
|
||||||
err = ioutil.WriteFile(path+"/"+tmpfile, []byte("foo\n"), 0644)
|
err = ioutil.WriteFile(path+"/"+tmpfile, []byte("foo\n"), 0644)
|
||||||
|
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
return repo
|
return repo
|
||||||
|
@ -55,6 +57,35 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) {
|
||||||
return commitId, treeId
|
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(path.Join(path.Dir(path.Dir(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) {
|
func TestOidZero(t *testing.T) {
|
||||||
var zeroId Oid
|
var zeroId Oid
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
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)
|
||||||
|
patch.ptr = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (patch *Patch) String() (string, error) {
|
||||||
|
if patch.ptr == nil {
|
||||||
|
return "", ErrInvalid
|
||||||
|
}
|
||||||
|
var buf C.git_buf
|
||||||
|
ecode := C.git_patch_to_buf(&buf, patch.ptr)
|
||||||
|
if ecode < 0 {
|
||||||
|
return "", MakeGitError(ecode)
|
||||||
|
}
|
||||||
|
return C.GoString(buf.ptr), nil
|
||||||
|
}
|
|
@ -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, nil)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -162,6 +162,20 @@ func (v *Repository) LookupReference(name string) (*Reference, error) {
|
||||||
return newReferenceFromC(ptr), nil
|
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, MakeGitError(ecode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newReferenceFromC(ptr), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Repository) CreateReference(name string, id *Oid, force bool, sig *Signature, msg string) (*Reference, error) {
|
func (v *Repository) CreateReference(name string, id *Oid, force bool, sig *Signature, msg string) (*Reference, error) {
|
||||||
cname := C.CString(name)
|
cname := C.CString(name)
|
||||||
defer C.free(unsafe.Pointer(cname))
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
25
wrapper.c
25
wrapper.c
|
@ -43,6 +43,31 @@ void _go_git_refdb_backend_free(git_refdb_backend *backend)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_diff_notify_callbacks(git_diff_options *opts) {
|
||||||
|
opts->notify_cb = (git_diff_notify_cb)diffNotifyCb;
|
||||||
|
}
|
||||||
|
|
||||||
void _go_git_setup_callbacks(git_remote_callbacks *callbacks) {
|
void _go_git_setup_callbacks(git_remote_callbacks *callbacks) {
|
||||||
typedef int (*completion_cb)(git_remote_completion_type type, void *data);
|
typedef int (*completion_cb)(git_remote_completion_type type, void *data);
|
||||||
typedef int (*credentials_cb)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data);
|
typedef int (*credentials_cb)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data);
|
||||||
|
|
Loading…
Reference in New Issue