diff options
author | nodivbyzero <nodivbyzero@gmail.com> | 2023-12-15 07:04:28 -0800 |
---|---|---|
committer | nodivbyzero <nodivbyzero@gmail.com> | 2024-03-11 13:21:50 -0700 |
commit | d058d588e5f7078c44ba42cfd7d2af61e58c2856 (patch) | |
tree | 62c74d11974519a5da5fa6483b6da06a47c3c0d4 | |
parent | 02bed284891e49ff594354101c897a29af3dbb43 (diff) | |
download | go-git-d058d588e5f7078c44ba42cfd7d2af61e58c2856.tar.gz |
plumbing: no panic in printStat function. Fixes #177
-rw-r--r-- | plumbing/object/commit_test.go | 2 | ||||
-rw-r--r-- | plumbing/object/patch.go | 95 | ||||
-rw-r--r-- | plumbing/object/patch_test.go | 110 |
3 files changed, 152 insertions, 55 deletions
diff --git a/plumbing/object/commit_test.go b/plumbing/object/commit_test.go index 3e1fe1b..2127e9a 100644 --- a/plumbing/object/commit_test.go +++ b/plumbing/object/commit_test.go @@ -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) { diff --git a/plumbing/object/patch.go b/plumbing/object/patch.go index dd8fef4..3c61f62 100644 --- a/plumbing/object/patch.go +++ b/plumbing/object/patch.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" "io" - "math" + "strconv" "strings" "github.com/go-git/go-git/v5/plumbing" @@ -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 { diff --git a/plumbing/object/patch_test.go b/plumbing/object/patch_test.go index 2cff795..e0e63a5 100644 --- a/plumbing/object/patch_test.go +++ b/plumbing/object/patch_test.go @@ -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) + } +} |