Add BlameFile func and options for git-blaming files
This commit is contained in:
parent
1796304374
commit
65ff78dead
|
@ -0,0 +1,138 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
#include <git2/blame.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
type BlameOptions struct {
|
||||||
|
Flags BlameOptionsFlag
|
||||||
|
MinMatchCharacters uint16
|
||||||
|
NewestCommit *Oid
|
||||||
|
OldestCommit *Oid
|
||||||
|
MinLine uint32
|
||||||
|
MaxLine uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultBlameOptions() (BlameOptions, error) {
|
||||||
|
opts := C.git_blame_options{}
|
||||||
|
ecode := C.git_blame_init_options(&opts, C.GIT_BLAME_OPTIONS_VERSION)
|
||||||
|
if ecode < 0 {
|
||||||
|
return BlameOptions{}, MakeGitError(ecode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlameOptions{
|
||||||
|
Flags: BlameOptionsFlag(opts.flags),
|
||||||
|
MinMatchCharacters: uint16(opts.min_match_characters),
|
||||||
|
NewestCommit: newOidFromC(&opts.newest_commit),
|
||||||
|
OldestCommit: newOidFromC(&opts.oldest_commit),
|
||||||
|
MinLine: uint32(opts.min_line),
|
||||||
|
MaxLine: uint32(opts.max_line),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlameOptionsFlag uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlameNormal BlameOptionsFlag = C.GIT_BLAME_NORMAL
|
||||||
|
BlameTrackCopiesSameFile BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE
|
||||||
|
BlameTrackCopiesSameCommitMoves BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES
|
||||||
|
BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES
|
||||||
|
BlameTrackCopiesAnyCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES
|
||||||
|
BlameFirstParent BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) {
|
||||||
|
var blamePtr *C.git_blame
|
||||||
|
|
||||||
|
var copts *C.git_blame_options
|
||||||
|
if opts != nil {
|
||||||
|
copts = &C.git_blame_options{
|
||||||
|
version: C.GIT_BLAME_OPTIONS_VERSION,
|
||||||
|
flags: C.uint32_t(opts.Flags),
|
||||||
|
min_match_characters: C.uint16_t(opts.MinMatchCharacters),
|
||||||
|
min_line: C.uint32_t(opts.MinLine),
|
||||||
|
max_line: C.uint32_t(opts.MaxLine),
|
||||||
|
}
|
||||||
|
if opts.NewestCommit != nil {
|
||||||
|
copts.newest_commit = *opts.NewestCommit.toC()
|
||||||
|
}
|
||||||
|
if opts.OldestCommit != nil {
|
||||||
|
copts.oldest_commit = *opts.OldestCommit.toC()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ecode := C.git_blame_file(&blamePtr, v.ptr, C.CString(path), copts)
|
||||||
|
if ecode < 0 {
|
||||||
|
return nil, MakeGitError(ecode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBlameFromC(blamePtr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Blame struct {
|
||||||
|
ptr *C.git_blame
|
||||||
|
}
|
||||||
|
|
||||||
|
func (blame *Blame) HunkCount() int {
|
||||||
|
return int(C.git_blame_get_hunk_count(blame.ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) {
|
||||||
|
ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index))
|
||||||
|
if ptr == nil {
|
||||||
|
return BlameHunk{}, ErrInvalid
|
||||||
|
}
|
||||||
|
return blameHunkFromC(ptr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBlameFromC(ptr *C.git_blame) *Blame {
|
||||||
|
if ptr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
blame := &Blame{
|
||||||
|
ptr: ptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.SetFinalizer(blame, (*Blame).Free)
|
||||||
|
return blame
|
||||||
|
}
|
||||||
|
|
||||||
|
func (blame *Blame) Free() error {
|
||||||
|
if blame.ptr == nil {
|
||||||
|
return ErrInvalid
|
||||||
|
}
|
||||||
|
runtime.SetFinalizer(blame, nil)
|
||||||
|
C.git_blame_free(blame.ptr)
|
||||||
|
blame.ptr = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlameHunk struct {
|
||||||
|
LinesInHunk uint16
|
||||||
|
FinalCommitId *Oid
|
||||||
|
FinalStartLineNumber uint16
|
||||||
|
FinalSignature *Signature
|
||||||
|
OrigCommitId *Oid
|
||||||
|
OrigPath string
|
||||||
|
OrigStartLineNumber uint16
|
||||||
|
OrigSignature *Signature
|
||||||
|
Boundary bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk {
|
||||||
|
return BlameHunk{
|
||||||
|
LinesInHunk: uint16(hunk.lines_in_hunk),
|
||||||
|
FinalCommitId: newOidFromC(&hunk.final_commit_id),
|
||||||
|
FinalStartLineNumber: uint16(hunk.final_start_line_number),
|
||||||
|
FinalSignature: newSignatureFromC(hunk.final_signature),
|
||||||
|
OrigCommitId: newOidFromC(&hunk.orig_commit_id),
|
||||||
|
OrigPath: C.GoString(hunk.orig_path),
|
||||||
|
OrigStartLineNumber: uint16(hunk.orig_start_line_number),
|
||||||
|
OrigSignature: newSignatureFromC(hunk.orig_signature),
|
||||||
|
Boundary: hunk.boundary == 1,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBlame(t *testing.T) {
|
||||||
|
repo := createTestRepo(t)
|
||||||
|
defer repo.Free()
|
||||||
|
defer os.RemoveAll(repo.Workdir())
|
||||||
|
|
||||||
|
commitId1, _ := seedTestRepo(t, repo)
|
||||||
|
commitId2, _ := updateReadme(t, repo, "foo\nbar\nbaz\n")
|
||||||
|
|
||||||
|
opts := BlameOptions{
|
||||||
|
NewestCommit: commitId2,
|
||||||
|
OldestCommit: nil,
|
||||||
|
MinLine: 1,
|
||||||
|
MaxLine: 3,
|
||||||
|
}
|
||||||
|
blame, err := repo.BlameFile("README", &opts)
|
||||||
|
checkFatal(t, err)
|
||||||
|
defer blame.Free()
|
||||||
|
if blame.HunkCount() != 2 {
|
||||||
|
t.Errorf("got hunk count %d, want 2", blame.HunkCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
hunk1, err := blame.HunkByIndex(0)
|
||||||
|
checkFatal(t, err)
|
||||||
|
checkHunk(t, hunk1, BlameHunk{
|
||||||
|
LinesInHunk: 1,
|
||||||
|
FinalCommitId: commitId1,
|
||||||
|
FinalStartLineNumber: 1,
|
||||||
|
OrigCommitId: commitId1,
|
||||||
|
OrigPath: "README",
|
||||||
|
OrigStartLineNumber: 1,
|
||||||
|
Boundary: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
hunk2, err := blame.HunkByIndex(1)
|
||||||
|
checkFatal(t, err)
|
||||||
|
checkHunk(t, hunk2, BlameHunk{
|
||||||
|
LinesInHunk: 2,
|
||||||
|
FinalCommitId: commitId2,
|
||||||
|
FinalStartLineNumber: 2,
|
||||||
|
OrigCommitId: commitId2,
|
||||||
|
OrigPath: "README",
|
||||||
|
OrigStartLineNumber: 2,
|
||||||
|
Boundary: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHunk(t *testing.T, hunk, want BlameHunk) {
|
||||||
|
hunk.FinalSignature = nil
|
||||||
|
want.FinalSignature = nil
|
||||||
|
hunk.OrigSignature = nil
|
||||||
|
want.OrigSignature = nil
|
||||||
|
if !reflect.DeepEqual(hunk, want) {
|
||||||
|
t.Fatalf("got hunk %+v, want %+v", hunk, want)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue