@@ -448,12 +448,20 @@ func getCommitFileLineCount(commit *git.Commit, filePath string) int {
448448 return lineCount
449449}
450450
451+ type FileTreeNode struct {
452+ IsFile bool
453+ Name string
454+ File * DiffFile
455+ Children []* FileTreeNode
456+ }
457+
451458// Diff represents a difference between two git trees.
452459type Diff struct {
453460 Start , End string
454461 NumFiles int
455462 TotalAddition , TotalDeletion int
456463 Files []* DiffFile
464+ FileTree []* FileTreeNode
457465 IsIncomplete bool
458466 NumViewedFiles int // user-specific
459467}
@@ -1212,6 +1220,8 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
12121220 }
12131221 }
12141222
1223+ diff .FileTree = buildTree (diff .Files )
1224+
12151225 if opts .FileOnly {
12161226 return diff , nil
12171227 }
@@ -1384,3 +1394,65 @@ func GetWhitespaceFlag(whitespaceBehavior string) git.TrustedCmdArgs {
13841394 log .Warn ("unknown whitespace behavior: %q, default to 'show-all'" , whitespaceBehavior )
13851395 return nil
13861396}
1397+
1398+ func buildTree (files []* DiffFile ) []* FileTreeNode {
1399+ result := make (map [string ]* FileTreeNode )
1400+ for _ , file := range files {
1401+ splits := strings .Split (file .Name , "/" )
1402+ currentNode := & FileTreeNode {Name : splits [0 ], IsFile : false }
1403+ if _ , exists := result [splits [0 ]]; ! exists {
1404+ result [splits [0 ]] = currentNode
1405+ } else {
1406+ currentNode = result [splits [0 ]]
1407+ }
1408+
1409+ parent := currentNode
1410+ for _ , split := range splits [1 :] {
1411+ found := false
1412+ for _ , child := range parent .Children {
1413+ if child .Name == split {
1414+ parent = child
1415+ found = true
1416+ break
1417+ }
1418+ }
1419+ if ! found {
1420+ newNode := & FileTreeNode {Name : split , IsFile : false }
1421+ parent .Children = append (parent .Children , newNode )
1422+ parent = newNode
1423+ }
1424+ }
1425+
1426+ lastNode := parent
1427+ lastNode .IsFile = true
1428+ lastNode .File = file
1429+ }
1430+
1431+ var roots []* FileTreeNode
1432+ for _ , node := range result {
1433+ if len (node .Children ) > 0 {
1434+ mergedNode := mergeSingleChildDirs (node )
1435+ roots = append (roots , mergedNode )
1436+ }
1437+ }
1438+ return roots
1439+ }
1440+
1441+ func mergeSingleChildDirs (node * FileTreeNode ) * FileTreeNode {
1442+ if len (node .Children ) == 1 && ! node .Children [0 ].IsFile {
1443+ merged := & FileTreeNode {
1444+ Name : fmt .Sprintf ("%s/%s" , node .Name , node .Children [0 ].Name ),
1445+ Children : node .Children [0 ].Children ,
1446+ IsFile : node .Children [0 ].IsFile ,
1447+ File : node .Children [0 ].File ,
1448+ }
1449+ if merged .File != nil {
1450+ merged .IsFile = true
1451+ }
1452+ return merged
1453+ }
1454+ for _ , child := range node .Children {
1455+ mergeSingleChildDirs (child )
1456+ }
1457+ return node
1458+ }
0 commit comments