| 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 |
| } |