diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 2ade845590..475c62ab6c 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -733,6 +733,9 @@ LEVEL = Info ;DISABLE_CORE_PROTECT_NTFS=false ;; Disable the usage of using partial clones for git. ;DISABLE_PARTIAL_CLONE = false +;; Set the similarity threshold passed to git commands via `--find-renames=`. +;; Default is 50%, the same as git. Must be a integer percentage between 0% and 100%. +;DIFF_RENAME_SIMILARITY_THRESHOLD = 50% ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Git Operation timeout in seconds diff --git a/modules/git/diff.go b/modules/git/diff.go index c97a2141bf..309d8f4615 100644 --- a/modules/git/diff.go +++ b/modules/git/diff.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" ) // RawDiffType type of a raw diff. @@ -47,7 +48,9 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff switch diffType { case RawDiffNormal: if len(startCommit) != 0 { - cmd.AddArguments("diff", "-M").AddDynamicArguments(startCommit, endCommit).AddDashesAndList(files...) + cmd.AddArguments("diff"). + AddOptionFormat("--find-renames=%s", setting.Git.DiffRenameSimilarityThreshold). + AddDynamicArguments(startCommit, endCommit).AddDashesAndList(files...) } else if commit.ParentCount() == 0 { cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...) } else { @@ -55,7 +58,9 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff if err != nil { return err } - cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...) + cmd.AddArguments("diff"). + AddOptionFormat("--find-renames=%s", setting.Git.DiffRenameSimilarityThreshold). + AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...) } case RawDiffPatch: if len(startCommit) != 0 { diff --git a/modules/setting/git.go b/modules/setting/git.go index 48a4e7f30d..318f2c0cac 100644 --- a/modules/setting/git.go +++ b/modules/setting/git.go @@ -5,6 +5,7 @@ package setting import ( "path/filepath" + "regexp" "strings" "time" @@ -17,20 +18,21 @@ var Git = struct { HomePath string DisableDiffHighlight bool - MaxGitDiffLines int - MaxGitDiffLineCharacters int - MaxGitDiffFiles int - CommitsRangeSize int // CommitsRangeSize the default commits range size - BranchesRangeSize int // BranchesRangeSize the default branches range size - VerbosePush bool - VerbosePushDelay time.Duration - GCArgs []string `ini:"GC_ARGS" delim:" "` - EnableAutoGitWireProtocol bool - PullRequestPushMessage bool - LargeObjectThreshold int64 - DisableCoreProtectNTFS bool - DisablePartialClone bool - Timeout struct { + MaxGitDiffLines int + MaxGitDiffLineCharacters int + MaxGitDiffFiles int + CommitsRangeSize int // CommitsRangeSize the default commits range size + BranchesRangeSize int // BranchesRangeSize the default branches range size + VerbosePush bool + VerbosePushDelay time.Duration + GCArgs []string `ini:"GC_ARGS" delim:" "` + EnableAutoGitWireProtocol bool + PullRequestPushMessage bool + LargeObjectThreshold int64 + DisableCoreProtectNTFS bool + DisablePartialClone bool + DiffRenameSimilarityThreshold string + Timeout struct { Default int Migrate int Mirror int @@ -39,19 +41,20 @@ var Git = struct { GC int `ini:"GC"` } `ini:"git.timeout"` }{ - DisableDiffHighlight: false, - MaxGitDiffLines: 1000, - MaxGitDiffLineCharacters: 5000, - MaxGitDiffFiles: 100, - CommitsRangeSize: 50, - BranchesRangeSize: 20, - VerbosePush: true, - VerbosePushDelay: 5 * time.Second, - GCArgs: []string{}, - EnableAutoGitWireProtocol: true, - PullRequestPushMessage: true, - LargeObjectThreshold: 1024 * 1024, - DisablePartialClone: false, + DisableDiffHighlight: false, + MaxGitDiffLines: 1000, + MaxGitDiffLineCharacters: 5000, + MaxGitDiffFiles: 100, + CommitsRangeSize: 50, + BranchesRangeSize: 20, + VerbosePush: true, + VerbosePushDelay: 5 * time.Second, + GCArgs: []string{}, + EnableAutoGitWireProtocol: true, + PullRequestPushMessage: true, + LargeObjectThreshold: 1024 * 1024, + DisablePartialClone: false, + DiffRenameSimilarityThreshold: "50%", Timeout: struct { Default int Migrate int @@ -117,4 +120,9 @@ func loadGitFrom(rootCfg ConfigProvider) { } else { Git.HomePath = filepath.Clean(Git.HomePath) } + + // validate for a integer percentage between 0% and 100% + if !regexp.MustCompile(`^([0-9]|[1-9][0-9]|100)%$`).MatchString(Git.DiffRenameSimilarityThreshold) { + log.Fatal("Invalid git.DIFF_RENAME_SIMILARITY_THRESHOLD: %s", Git.DiffRenameSimilarityThreshold) + } } diff --git a/services/gitdiff/git_diff_tree.go b/services/gitdiff/git_diff_tree.go index 4649c24af3..2a3c7c9445 100644 --- a/services/gitdiff/git_diff_tree.go +++ b/services/gitdiff/git_diff_tree.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" ) type DiffTree struct { @@ -56,7 +57,9 @@ func runGitDiffTree(ctx context.Context, gitRepo *git.Repository, useMergeBase b return nil, err } - cmd := gitcmd.NewCommand("diff-tree", "--raw", "-r", "--find-renames", "--root") + cmd := gitcmd.NewCommand("diff-tree", "--raw", "-r", "--root"). + AddOptionFormat("--find-renames=%s", setting.Git.DiffRenameSimilarityThreshold) + if useMergeBase { cmd.AddArguments("--merge-base") } diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 34e94671a2..17eb3d4280 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1225,8 +1225,9 @@ func getDiffBasic(ctx context.Context, gitRepo *git.Repository, opts *DiffOption } cmdDiff := gitcmd.NewCommand(). - AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"). - AddArguments(opts.WhitespaceBehavior...) + AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/"). + AddArguments(opts.WhitespaceBehavior...). + AddOptionFormat("--find-renames=%s", setting.Git.DiffRenameSimilarityThreshold) // In git 2.31, git diff learned --skip-to which we can use to shortcut skip to file // so if we are using at least this version of git we don't have to tell ParsePatch to do