diff --git a/diff.go b/diff.go index f762a56..661a9a7 100644 --- a/diff.go +++ b/diff.go @@ -164,6 +164,29 @@ func (diff *Diff) Free() error { return nil } +func (diff *Diff) FindSimilar(opts *DiffFindOptions) error { + + var copts *C.git_diff_find_options + if opts != nil { + copts = &C.git_diff_find_options{ + version: C.GIT_DIFF_FIND_OPTIONS_VERSION, + flags: C.uint32_t(opts.Flags), + rename_threshold: C.uint16_t(opts.RenameThreshold), + copy_threshold: C.uint16_t(opts.CopyThreshold), + rename_from_rewrite_threshold: C.uint16_t(opts.RenameFromRewriteThreshold), + break_rewrite_threshold: C.uint16_t(opts.BreakRewriteThreshold), + rename_limit: C.size_t(opts.RenameLimit), + } + } + + ecode := C.git_diff_find_similar(diff.ptr, copts) + if ecode < 0 { + return MakeGitError(ecode) + } + + return nil +} + type diffForEachData struct { FileCallback DiffForEachFileCallback HunkCallback DiffForEachHunkCallback @@ -341,6 +364,54 @@ func DefaultDiffOptions() (DiffOptions, error) { }, nil } +type DiffFindOptionsFlag int + +const ( + DiffFindByConfig DiffFindOptionsFlag = C.GIT_DIFF_FIND_BY_CONFIG + DiffFindRenames DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES + DiffFindRenamesFromRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES_FROM_REWRITES + DiffFindCopies DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES + DiffFindCopiesFromUnmodified DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED + DiffFindRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_REWRITES + DiffFindBreakRewrites DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES + DiffFindAndBreakRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_AND_BREAK_REWRITES + DiffFindForUntracked DiffFindOptionsFlag = C.GIT_DIFF_FIND_FOR_UNTRACKED + DiffFindAll DiffFindOptionsFlag = C.GIT_DIFF_FIND_ALL + DiffFindIgnoreLeadingWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE + DiffFindIgnoreWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_WHITESPACE + DiffFindDontIgnoreWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE + DiffFindExactMatchOnly DiffFindOptionsFlag = C.GIT_DIFF_FIND_EXACT_MATCH_ONLY + DiffFindBreakRewritesForRenamesOnly DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY + DiffFindRemoveUnmodified DiffFindOptionsFlag = C.GIT_DIFF_FIND_REMOVE_UNMODIFIED +) + +//TODO implement git_diff_similarity_metric +type DiffFindOptions struct { + Flags DiffFindOptionsFlag + RenameThreshold uint16 + CopyThreshold uint16 + RenameFromRewriteThreshold uint16 + BreakRewriteThreshold uint16 + RenameLimit uint +} + +func DefaultDiffFindOptions() (DiffFindOptions, error) { + opts := C.git_diff_find_options{} + ecode := C.git_diff_find_init_options(&opts, C.GIT_DIFF_FIND_OPTIONS_VERSION) + if ecode < 0 { + return DiffFindOptions{}, MakeGitError(ecode) + } + + return DiffFindOptions{ + Flags: DiffFindOptionsFlag(opts.flags), + RenameThreshold: uint16(opts.rename_threshold), + CopyThreshold: uint16(opts.copy_threshold), + RenameFromRewriteThreshold: uint16(opts.rename_from_rewrite_threshold), + BreakRewriteThreshold: uint16(opts.break_rewrite_threshold), + RenameLimit: uint(opts.rename_limit), + }, nil +} + var ( ErrDeltaSkip = errors.New("Skip delta") ) diff --git a/diff_test.go b/diff_test.go index b688294..84d72db 100644 --- a/diff_test.go +++ b/diff_test.go @@ -6,20 +6,68 @@ import ( "testing" ) -func TestDiffTreeToTree(t *testing.T) { +func TestFindSimilar(t *testing.T) { repo := createTestRepo(t) defer repo.Free() defer os.RemoveAll(repo.Workdir()) - _, originalTreeId := seedTestRepo(t, repo) - originalTree, err := repo.LookupTree(originalTreeId) + originalTree, newTree := createTestTrees(t, repo) + diffOpt, _ := DefaultDiffOptions() + + diff, err := repo.DiffTreeToTree(originalTree, newTree, &diffOpt) + checkFatal(t, err) + if diff == nil { + t.Fatal("no diff returned") + } + + findOpts, err := DefaultDiffFindOptions() + checkFatal(t, err) + findOpts.Flags = DiffFindBreakRewrites + + err = diff.FindSimilar(&findOpts) checkFatal(t, err) - _, newTreeId := updateReadme(t, repo, "file changed\n") + numDiffs := 0 + numAdded := 0 + numDeleted := 0 - newTree, err := repo.LookupTree(newTreeId) - checkFatal(t, err) + err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { + numDiffs++ + + switch file.Status { + case DeltaAdded: + numAdded++ + case DeltaDeleted: + numDeleted++ + } + + return func(hunk DiffHunk) (DiffForEachLineCallback, error) { + return func(line DiffLine) error { + return nil + }, nil + }, nil + }, DiffDetailLines) + + if numDiffs != 2 { + t.Fatal("Incorrect number of files in diff") + } + if numAdded != 1 { + t.Fatal("Incorrect number of new files in diff") + } + if numDeleted != 1 { + t.Fatal("Incorrect number of deleted files in diff") + } + +} + +func TestDiffTreeToTree(t *testing.T) { + + repo := createTestRepo(t) + defer repo.Free() + defer os.RemoveAll(repo.Workdir()) + + originalTree, newTree := createTestTrees(t, repo) callbackInvoked := false opts := DiffOptions{ @@ -94,3 +142,18 @@ func TestDiffTreeToTree(t *testing.T) { } } + +func createTestTrees(t *testing.T, repo *Repository) (originalTree *Tree, newTree *Tree) { + var err error + _, 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) + + return originalTree, newTree +}