77 "bufio"
88 "context"
99 "fmt"
10+ "io"
1011 "strings"
1112
1213 "code.gitea.io/gitea/modules/git"
@@ -29,9 +30,11 @@ type DiffTreeRecord struct {
2930 BaseBlobID string
3031}
3132
32- // GetDiffTree returns the list of path of the files that have changed between the two commits
33- func GetDiffTree (ctx context.Context , gitRepo * git.Repository , baseSha , headSha string ) (* DiffTree , error ) {
34- gitDiffTreeRecords , err := runGitDiffTree (ctx , gitRepo , baseSha , headSha )
33+ // GetDiffTree returns the list of path of the files that have changed between the two commits.
34+ // If useMergeBase is true, the diff will be calculated using the merge base of the two commits.
35+ // This is the same behavior as using a three-dot diff in git diff.
36+ func GetDiffTree (ctx context.Context , gitRepo * git.Repository , useMergeBase bool , baseSha , headSha string ) (* DiffTree , error ) {
37+ gitDiffTreeRecords , err := runGitDiffTree (ctx , gitRepo , useMergeBase , baseSha , headSha )
3538 if err != nil {
3639 return nil , err
3740 }
@@ -41,32 +44,36 @@ func GetDiffTree(ctx context.Context, gitRepo *git.Repository, baseSha, headSha
4144 }, nil
4245}
4346
44- func runGitDiffTree (ctx context.Context , gitRepo * git.Repository , baseSha , headSha string ) ([]* DiffTreeRecord , error ) {
45- baseCommitID , headCommitID , err := validateGitDiffTreeArguments (gitRepo , baseSha , headSha )
47+ func runGitDiffTree (ctx context.Context , gitRepo * git.Repository , useMergeBase bool , baseSha , headSha string ) ([]* DiffTreeRecord , error ) {
48+ useMergeBase , baseCommitID , headCommitID , err := validateGitDiffTreeArguments (gitRepo , useMergeBase , baseSha , headSha )
4649 if err != nil {
4750 return nil , err
4851 }
4952
50- cmd := git .NewCommand (ctx , "diff-tree" , "--raw" , "-r" , "--find-renames" ).AddDynamicArguments (baseCommitID , headCommitID )
53+ cmd := git .NewCommand (ctx , "diff-tree" , "--raw" , "-r" , "--find-renames" , "--root" )
54+ if useMergeBase {
55+ cmd .AddArguments ("--merge-base" )
56+ }
57+ cmd .AddDynamicArguments (baseCommitID , headCommitID )
5158 stdout , _ , runErr := cmd .RunStdString (& git.RunOpts {Dir : gitRepo .Path })
5259 if runErr != nil {
5360 log .Warn ("git diff-tree: %v" , runErr )
5461 return nil , runErr
5562 }
5663
57- return parseGitDiffTree (stdout )
64+ return parseGitDiffTree (strings . NewReader ( stdout ) )
5865}
5966
60- func validateGitDiffTreeArguments (gitRepo * git.Repository , baseSha , headSha string ) (string , string , error ) {
67+ func validateGitDiffTreeArguments (gitRepo * git.Repository , useMergeBase bool , baseSha , headSha string ) (bool , string , string , error ) {
6168 // if the head is empty its an error
6269 if headSha == "" {
63- return "" , "" , fmt .Errorf ("headSha is empty" )
70+ return false , "" , "" , fmt .Errorf ("headSha is empty" )
6471 }
6572
6673 // if the head commit doesn't exist its and error
6774 headCommit , err := gitRepo .GetCommit (headSha )
6875 if err != nil {
69- return "" , "" , fmt .Errorf ("failed to get commit headSha: %v" , err )
76+ return false , "" , "" , fmt .Errorf ("failed to get commit headSha: %v" , err )
7077 }
7178 headCommitID := headCommit .ID .String ()
7279
@@ -77,30 +84,31 @@ func validateGitDiffTreeArguments(gitRepo *git.Repository, baseSha, headSha stri
7784 if headCommit .ParentCount () == 0 {
7885 objectFormat , err := gitRepo .GetObjectFormat ()
7986 if err != nil {
80- return "" , "" , err
87+ return false , "" , "" , err
8188 }
8289
83- return objectFormat .EmptyTree ().String (), headCommitID , nil
90+ // We set use merge base to false because we have no base commit
91+ return false , objectFormat .EmptyTree ().String (), headCommitID , nil
8492 }
8593
8694 baseCommit , err := headCommit .Parent (0 )
8795 if err != nil {
88- return "" , "" , fmt .Errorf ("baseSha is '', attempted to use parent of commit %s, got error: %v" , headCommit .ID .String (), err )
96+ return false , "" , "" , fmt .Errorf ("baseSha is '', attempted to use parent of commit %s, got error: %v" , headCommit .ID .String (), err )
8997 }
90- return baseCommit .ID .String (), headCommitID , nil
98+ return useMergeBase , baseCommit .ID .String (), headCommitID , nil
9199 }
92100
93101 // try and get the base commit
94102 baseCommit , err := gitRepo .GetCommit (baseSha )
95103 // propagate the error if we couldn't get the base commit
96104 if err != nil {
97- return "" , "" , fmt .Errorf ("failed to get base commit %s: %v" , baseSha , err )
105+ return useMergeBase , "" , "" , fmt .Errorf ("failed to get base commit %s: %v" , baseSha , err )
98106 }
99107
100- return baseCommit .ID .String (), headCommit .ID .String (), nil
108+ return useMergeBase , baseCommit .ID .String (), headCommit .ID .String (), nil
101109}
102110
103- func parseGitDiffTree (output string ) ([]* DiffTreeRecord , error ) {
111+ func parseGitDiffTree (gitOutput io. Reader ) ([]* DiffTreeRecord , error ) {
104112 /*
105113 The output of `git diff-tree --raw -r --find-renames` is of the form:
106114
@@ -112,13 +120,9 @@ func parseGitDiffTree(output string) ([]*DiffTreeRecord, error) {
112120
113121 See: <https://git-scm.com/docs/git-diff-tree#_raw_output_format> for more details
114122 */
115- if output == "" {
116- return []* DiffTreeRecord {}, nil
117- }
118-
119123 results := make ([]* DiffTreeRecord , 0 )
120124
121- lines := bufio .NewScanner (strings . NewReader ( output ) )
125+ lines := bufio .NewScanner (gitOutput )
122126 for lines .Scan () {
123127 line := lines .Text ()
124128
0 commit comments