Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plumbing: no panic in printStats function. Fixes #177 #971

Merged
merged 1 commit into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
}
}