Skip to content

Commit

Permalink
plumbing: no panic in printStat function. Fixes #177
Browse files Browse the repository at this point in the history
  • Loading branch information
nodivbyzero committed Mar 11, 2024
1 parent 02bed28 commit d058d58
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 55 deletions.
2 changes: 1 addition & 1 deletion plumbing/object/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ func (s *SuiteCommit) TestStat(c *C) {
c.Assert(fileStats[1].Name, Equals, "php/crappy.php")
c.Assert(fileStats[1].Addition, Equals, 259)
c.Assert(fileStats[1].Deletion, Equals, 0)
c.Assert(fileStats[1].String(), Equals, " php/crappy.php | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n")
c.Assert(fileStats[1].String(), Equals, " php/crappy.php | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n")
}

func (s *SuiteCommit) TestVerify(c *C) {
Expand Down
95 changes: 41 additions & 54 deletions plumbing/object/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"errors"
"fmt"
"io"
"math"
"strconv"
"strings"

"github.com/go-git/go-git/v5/plumbing"
Expand Down Expand Up @@ -234,69 +234,56 @@ func (fileStats FileStats) String() string {
return printStat(fileStats)
}

// printStat prints the stats of changes in content of files.
// Original implementation: https://github.com/git/git/blob/1a87c842ece327d03d08096395969aca5e0a6996/diff.c#L2615
// Parts of the output:
// <pad><filename><pad>|<pad><changeNumber><pad><+++/---><newline>
// example: " main.go | 10 +++++++--- "
func printStat(fileStats []FileStat) string {
padLength := float64(len(" "))
newlineLength := float64(len("\n"))
separatorLength := float64(len("|"))
// Soft line length limit. The text length calculation below excludes
// length of the change number. Adding that would take it closer to 80,
// but probably not more than 80, until it's a huge number.
lineLength := 72.0

// Get the longest filename and longest total change.
var longestLength float64
var longestTotalChange float64
for _, fs := range fileStats {
if int(longestLength) < len(fs.Name) {
longestLength = float64(len(fs.Name))
}
totalChange := fs.Addition + fs.Deletion
if int(longestTotalChange) < totalChange {
longestTotalChange = float64(totalChange)
}
}

// Parts of the output:
// <pad><filename><pad>|<pad><changeNumber><pad><+++/---><newline>
// example: " main.go | 10 +++++++--- "

// <pad><filename><pad>
leftTextLength := padLength + longestLength + padLength

// <pad><number><pad><+++++/-----><newline>
// Excluding number length here.
rightTextLength := padLength + padLength + newlineLength
maxGraphWidth := uint(53)
maxNameLen := 0
maxChangeLen := 0

totalTextArea := leftTextLength + separatorLength + rightTextLength
heightOfHistogram := lineLength - totalTextArea
scaleLinear := func(it, width, max uint) uint {
if it == 0 || max == 0 {
return 0
}

// Scale the histogram.
var scaleFactor float64
if longestTotalChange > heightOfHistogram {
// Scale down to heightOfHistogram.
scaleFactor = longestTotalChange / heightOfHistogram
} else {
scaleFactor = 1.0
return 1 + (it * (width - 1) / max)
}

finalOutput := ""
for _, fs := range fileStats {
addn := float64(fs.Addition)
deln := float64(fs.Deletion)
addc := int(math.Floor(addn/scaleFactor))
delc := int(math.Floor(deln/scaleFactor))
if addc < 0 {
addc = 0
if len(fs.Name) > maxNameLen {
maxNameLen = len(fs.Name)
}
if delc < 0 {
delc = 0

changes := strconv.Itoa(fs.Addition + fs.Deletion)
if len(changes) > maxChangeLen {
maxChangeLen = len(changes)
}
adds := strings.Repeat("+", addc)
dels := strings.Repeat("-", delc)
finalOutput += fmt.Sprintf(" %s | %d %s%s\n", fs.Name, (fs.Addition + fs.Deletion), adds, dels)
}

return finalOutput
result := ""
for _, fs := range fileStats {
add := uint(fs.Addition)
del := uint(fs.Deletion)
np := maxNameLen - len(fs.Name)
cp := maxChangeLen - len(strconv.Itoa(fs.Addition+fs.Deletion))

total := add + del
if total > maxGraphWidth {
add = scaleLinear(add, maxGraphWidth, total)
del = scaleLinear(del, maxGraphWidth, total)
}

adds := strings.Repeat("+", int(add))
dels := strings.Repeat("-", int(del))
namePad := strings.Repeat(" ", np)
changePad := strings.Repeat(" ", cp)

result += fmt.Sprintf(" %s%s | %s%d %s%s\n", fs.Name, namePad, changePad, total, adds, dels)
}
return result
}

func getFileStatsFromFilePatches(filePatches []fdiff.FilePatch) FileStats {
Expand Down
110 changes: 110 additions & 0 deletions plumbing/object/patch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,113 @@ func (s *PatchSuite) TestStatsWithSubmodules(c *C) {
c.Assert(err, IsNil)
c.Assert(p, NotNil)
}

func (s *PatchSuite) TestFileStatsString(c *C) {
testCases := []struct {
description string
input FileStats
expected string
}{

{
description: "no files changed",
input: []FileStat{},
expected: "",
},
{
description: "one file touched - no changes",
input: []FileStat{
{
Name: "file1",
},
},
expected: " file1 | 0 \n",
},
{
description: "one file changed",
input: []FileStat{
{
Name: "file1",
Addition: 1,
},
},
expected: " file1 | 1 +\n",
},
{
description: "one file changed with one addition and one deletion",
input: []FileStat{
{
Name: ".github/workflows/git.yml",
Addition: 1,
Deletion: 1,
},
},
expected: " .github/workflows/git.yml | 2 +-\n",
},
{
description: "two files changed",
input: []FileStat{
{
Name: ".github/workflows/git.yml",
Addition: 1,
Deletion: 1,
},
{
Name: "cli/go-git/go.mod",
Addition: 4,
Deletion: 4,
},
},
expected: " .github/workflows/git.yml | 2 +-\n cli/go-git/go.mod | 8 ++++----\n",
},
{
description: "three files changed",
input: []FileStat{
{
Name: ".github/workflows/git.yml",
Addition: 3,
Deletion: 3,
},
{
Name: "worktree.go",
Addition: 107,
},
{
Name: "worktree_test.go",
Addition: 75,
},
},
expected: " .github/workflows/git.yml | 6 +++---\n" +
" worktree.go | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n" +
" worktree_test.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n",
},
{
description: "three files changed with deletions and additions",
input: []FileStat{
{
Name: ".github/workflows/git.yml",
Addition: 3,
Deletion: 3,
},
{
Name: "worktree.go",
Addition: 107,
Deletion: 217,
},
{
Name: "worktree_test.go",
Addition: 75,
Deletion: 275,
},
},
expected: " .github/workflows/git.yml | 6 +++---\n" +
" worktree.go | 324 ++++++++++++++++++-----------------------------------\n" +
" worktree_test.go | 350 ++++++++++++-----------------------------------------\n",
},
}

for _, tc := range testCases {
c.Log("Executing test cases:", tc.description)
c.Assert(printStat(tc.input), Equals, tc.expected)
}
}

0 comments on commit d058d58

Please sign in to comment.