From 11bba03fa9d2b588786dbcf827844b6dca1ef801 Mon Sep 17 00:00:00 2001 From: purpleclay Date: Sat, 17 Aug 2024 07:17:23 +0100 Subject: [PATCH 1/3] support git diffs --- diff.go | 231 +++++++++++++++++++++++++++++++++++++ diff_test.go | 68 +++++++++++ gittest/log.go | 22 ---- gittest/log_test.go | 22 ---- gittest/repository.go | 22 ---- gittest/repository_test.go | 22 ---- go.mod | 1 + go.sum | 2 + scan/scanner.go | 51 ++++---- scan/scanner_test.go | 80 +++++++++---- 10 files changed, 388 insertions(+), 133 deletions(-) create mode 100644 diff.go create mode 100644 diff_test.go diff --git a/diff.go b/diff.go new file mode 100644 index 0000000..ae5fd06 --- /dev/null +++ b/diff.go @@ -0,0 +1,231 @@ +package git + +import ( + "bufio" + "strconv" + "strings" + + "github.com/purpleclay/chomp" + "github.com/purpleclay/gitz/scan" +) + +const ( + // git diff header delimiter > @@ ... @@ + hdrDelim = "@@" + // prefix for lines added + addPrefix = "+" + // prefix for lines removed + remPrefix = "-" +) + +// CommitOption provides a way for setting specific options during a commit +// operation. Each supported option can customize the way the commit is +// created against the current repository (working directory) + +// DiffOption ... +type DiffOption func(*diffOptions) + +type diffOptions struct { + DiffPaths []string +} + +// WithDiffPaths ... +func WithDiffPaths(paths ...string) DiffOption { + return func(opts *diffOptions) { + opts.DiffPaths = trim(paths...) + } +} + +// FileDiff ... +type FileDiff struct { + Path string + Chunks []DiffChunk +} + +// DiffChunk ... +type DiffChunk struct { + Added DiffChange + Removed DiffChange +} + +// DiffChange ... +type DiffChange struct { + LineNo int + Count int + Change string +} + +// Diff ... +func (c *Client) Diff(opts ...DiffOption) ([]FileDiff, error) { + options := &diffOptions{} + for _, opt := range opts { + opt(options) + } + + var buf strings.Builder + buf.WriteString("git diff -U0 --no-color") + + if len(options.DiffPaths) > 0 { + buf.WriteString(" -- ") + buf.WriteString(strings.Join(options.DiffPaths, " ")) + } + + out, err := c.exec(buf.String()) + if err != nil { + return nil, err + } + return parseDiffs(out) +} + +func parseDiffs(log string) ([]FileDiff, error) { + var diffs []FileDiff + + scanner := bufio.NewScanner(strings.NewReader(log)) + scanner.Split(scan.DiffLines()) + + for scanner.Scan() { + diff, err := parseDiff(scanner.Text()) + if err != nil { + return nil, err + } + + diffs = append(diffs, diff) + } + + return diffs, nil +} + +func parseDiff(diff string) (FileDiff, error) { + rem, path, err := diffPath()(diff) + if err != nil { + return FileDiff{}, err + } + + rem, _, err = chomp.Until(hdrDelim)(rem) + if err != nil { + return FileDiff{}, err + } + + chunks, err := diffChunks(rem) + if err != nil { + return FileDiff{}, err + } + + return FileDiff{ + Path: path, + Chunks: chunks, + }, nil +} + +func diffPath() chomp.Combinator[string] { + return func(s string) (string, string, error) { + var rem string + var err error + + if rem, _, err = chomp.Tag("diff --git ")(s); err != nil { + return rem, "", err + } + + var path string + if rem, path, err = chomp.Until(" ")(rem); err != nil { + return rem, "", err + } + path = path[strings.Index(path, "/")+1:] + + rem, _, err = chomp.Eol()(rem) + return rem, path, err + } +} + +func diffChunks(in string) ([]DiffChunk, error) { + _, chunks, err := chomp.Map(chomp.Many(diffChunk()), + func(in []string) []DiffChunk { + var diffChunks []DiffChunk + + for i := 0; i+5 < len(in); i += 6 { + chunk := DiffChunk{ + Removed: DiffChange{ + LineNo: mustInt(in[i]), + Count: mustInt(in[i+1]), + Change: in[i+4], + }, + Added: DiffChange{ + LineNo: mustInt(in[i+2]), + Count: mustInt(in[i+3]), + Change: in[i+5], + }, + } + + if chunk.Added.Count == 0 { + chunk.Added.Count = 1 + } + + if chunk.Removed.Count == 0 { + chunk.Removed.Count = 1 + } + + diffChunks = append(diffChunks, chunk) + } + + return diffChunks + }, + )(in) + + return chunks, err +} + +func mustInt(in string) int { + out, _ := strconv.Atoi(in) + return out +} + +func diffChunk() chomp.Combinator[[]string] { + return func(s string) (string, []string, error) { + var rem string + var err error + + var changes []string + rem, changes, err = chomp.Delimited( + chomp.Tag(hdrDelim+" "), + chomp.SepPair(diffChunkHeaderChange(remPrefix), chomp.Tag(" "), diffChunkHeaderChange(addPrefix)), + chomp.Eol(), + )(s) + if err != nil { + return rem, nil, err + } + + var removed string + rem, removed, err = chomp.Map( + chomp.ManyN(chomp.Prefixed(chomp.Eol(), chomp.Tag(remPrefix)), 0), + func(in []string) string { return strings.Join(in, "\n") }, + )(rem) + if err != nil { + return rem, nil, err + } + + var added string + rem, added, err = chomp.Map( + chomp.ManyN(chomp.Prefixed(chomp.Eol(), chomp.Tag(addPrefix)), 0), + func(in []string) string { return strings.Join(in, "\n") }, + )(rem) + if err != nil { + return rem, nil, err + } + + return rem, append(changes, removed, added), nil + } +} + +func diffChunkHeaderChange(prefix string) chomp.Combinator[[]string] { + return func(s string) (string, []string, error) { + rem, _, err := chomp.Tag(prefix)(s) + if err != nil { + return rem, nil, err + } + + return chomp.All( + chomp.While(chomp.IsDigit), + chomp.Opt(chomp.Prefixed(chomp.While(chomp.IsDigit), chomp.Tag(","))), + )(rem) + } +} diff --git a/diff_test.go b/diff_test.go new file mode 100644 index 0000000..6757c42 --- /dev/null +++ b/diff_test.go @@ -0,0 +1,68 @@ +package git_test + +import ( + "testing" + + git "github.com/purpleclay/gitz" + "github.com/purpleclay/gitz/gittest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDiff(t *testing.T) { + gittest.InitRepository(t, + gittest.WithCommittedFiles("main.go"), + gittest.WithFileContent("main.go", `package main + +import "fmt" + +func print() { + fmt.Println("Hello, World!") +} + +func main() { + print() +}`)) + + overwriteFile(t, "main.go", `package main + +import ( + "fmt" + "os" +) + +func main() { + fmt.Printf("Hello, %s\n" + os.Args[1]) +}`) + + client, _ := git.NewClient() + diffs, err := client.Diff() + require.NoError(t, err) + + require.Len(t, diffs, 1) + assert.Equal(t, "main.go", diffs[0].Path) + + require.Len(t, diffs[0].Chunks, 2) + assert.Equal(t, 3, diffs[0].Chunks[0].Added.LineNo) + assert.Equal(t, 4, diffs[0].Chunks[0].Added.Count) + assert.Equal(t, `import ( + "fmt" + "os" +)`, diffs[0].Chunks[0].Added.Change) + + assert.Equal(t, 3, diffs[0].Chunks[0].Removed.LineNo) + assert.Equal(t, 5, diffs[0].Chunks[0].Removed.Count) + assert.Equal(t, `import "fmt" + +func print() { + fmt.Println("Hello, World!") +}`, diffs[0].Chunks[0].Removed.Change) + + assert.Equal(t, 9, diffs[0].Chunks[1].Added.LineNo) + assert.Equal(t, 1, diffs[0].Chunks[1].Added.Count) + assert.Equal(t, ` fmt.Printf("Hello, %s\n" + os.Args[1])`, diffs[0].Chunks[1].Added.Change) + + assert.Equal(t, 10, diffs[0].Chunks[1].Removed.LineNo) + assert.Equal(t, 1, diffs[0].Chunks[1].Removed.Count) + assert.Equal(t, ` print()`, diffs[0].Chunks[1].Removed.Change) +} diff --git a/gittest/log.go b/gittest/log.go index 800acfa..6ece76d 100644 --- a/gittest/log.go +++ b/gittest/log.go @@ -1,25 +1,3 @@ -/* -Copyright (c) 2023 Purple Clay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - package gittest import ( diff --git a/gittest/log_test.go b/gittest/log_test.go index 5718aa3..7a4f64d 100644 --- a/gittest/log_test.go +++ b/gittest/log_test.go @@ -1,25 +1,3 @@ -/* -Copyright (c) 2023 Purple Clay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - package gittest_test import ( diff --git a/gittest/repository.go b/gittest/repository.go index 9cae4b5..57ad8d0 100644 --- a/gittest/repository.go +++ b/gittest/repository.go @@ -1,25 +1,3 @@ -/* -Copyright (c) 2023 Purple Clay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - package gittest import ( diff --git a/gittest/repository_test.go b/gittest/repository_test.go index 154649f..e311b2f 100644 --- a/gittest/repository_test.go +++ b/gittest/repository_test.go @@ -1,25 +1,3 @@ -/* -Copyright (c) 2023 Purple Clay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - package gittest_test import ( diff --git a/go.mod b/go.mod index 0b83c5a..c90a2bc 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/purpleclay/gitz go 1.21 require ( + github.com/purpleclay/chomp v0.3.0 github.com/stretchr/testify v1.9.0 mvdan.cc/sh/v3 v3.8.0 ) diff --git a/go.sum b/go.sum index 41fea61..5b05741 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/purpleclay/chomp v0.3.0 h1:NJ4BA1CavAVssqxaAKgbl7501CusnBC6KXChB3Oexbk= +github.com/purpleclay/chomp v0.3.0/go.mod h1:2G5jE5JN68ytZSjGLOE7QkXuFgtkNRk7b58trMmB/nc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= diff --git a/scan/scanner.go b/scan/scanner.go index fdf8e52..02d20e5 100644 --- a/scan/scanner.go +++ b/scan/scanner.go @@ -1,28 +1,8 @@ -/* -Copyright (c) 2023 Purple Clay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - package scan -import "bytes" +import ( + "bytes" +) // PrefixedLines is a split function for a [bufio.Scanner] that returns // each block of text, stripped of both the prefix marker and any leading @@ -55,3 +35,28 @@ func eat(prefix byte, data []byte) []byte { return bytes.TrimSpace(data[i:]) } + +// DiffLines is a split function for a [bufio.Scanner] that splits a git diff output +// into multiple blocks of text, each prefixed by the diff --git marker. Each block +// of text will be stripped of any leading and trailing whitespace. If the git diff +// marker isn't detected, the entire block of text is returned, with any leading and +// trailing whitespace stripped +func DiffLines() func(data []byte, atEOF bool) (advance int, token []byte, err error) { + prefix := []byte("\ndiff --git") + + return func(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + + if i := bytes.Index(data, prefix); i >= 0 { + return i + 1, bytes.TrimSpace(data[:i]), nil + } + + if atEOF { + return len(data), bytes.TrimSpace(data), nil + } + + return 0, nil, nil + } +} diff --git a/scan/scanner_test.go b/scan/scanner_test.go index 5cda819..5f395ac 100644 --- a/scan/scanner_test.go +++ b/scan/scanner_test.go @@ -1,25 +1,3 @@ -/* -Copyright (c) 2023 Purple Clay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - package scan_test import ( @@ -105,3 +83,61 @@ this is line #2`, lines[0]) assert.Equal(t, `this is line #3 and it is spread over two lines`, lines[1]) } + +func TestDiffLines(t *testing.T) { + text := `diff --git a/clone.go b/clone.go +index f181e5f..bea7426 100644 +--- a/clone.go ++++ b/clone.go +@@ -10,6 +10,7 @@ import ( + // repository is cloned onto the file system into a target working directory + type CloneOption func(*cloneOptions) + ++// Hello + type cloneOptions struct { + Config []string + CheckoutRef string +diff --git a/commit.go b/commit.go +index 906a132..2e6954c 100644 +--- a/commit.go ++++ b/commit.go +@@ -10,6 +10,7 @@ import ( + // created against the current repository (working directory) + type CommitOption func(*commitOptions) + ++// Hello, again! + type commitOptions struct { + AllowEmpty bool + Config []string +` + + scanner := bufio.NewScanner(strings.NewReader(text)) + scanner.Split(scan.DiffLines()) + + lines := readUntilEOF(t, scanner) + require.Len(t, lines, 2) + assert.Equal(t, `diff --git a/clone.go b/clone.go +index f181e5f..bea7426 100644 +--- a/clone.go ++++ b/clone.go +@@ -10,6 +10,7 @@ import ( + // repository is cloned onto the file system into a target working directory + type CloneOption func(*cloneOptions) + ++// Hello + type cloneOptions struct { + Config []string + CheckoutRef string`, lines[0]) + assert.Equal(t, `diff --git a/commit.go b/commit.go +index 906a132..2e6954c 100644 +--- a/commit.go ++++ b/commit.go +@@ -10,6 +10,7 @@ import ( + // created against the current repository (working directory) + type CommitOption func(*commitOptions) + ++// Hello, again! + type commitOptions struct { + AllowEmpty bool + Config []string`, lines[1]) +} From 5226dbae08d838cfe0f9e3e0180f263a9410053d Mon Sep 17 00:00:00 2001 From: purpleclay Date: Sun, 18 Aug 2024 06:59:09 +0100 Subject: [PATCH 2/3] support git diffs --- diff.go | 52 +++++++++++++++++++++++++++++++++++++++------------- diff_test.go | 15 +++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/diff.go b/diff.go index ae5fd06..038e0c3 100644 --- a/diff.go +++ b/diff.go @@ -18,44 +18,70 @@ const ( remPrefix = "-" ) -// CommitOption provides a way for setting specific options during a commit -// operation. Each supported option can customize the way the commit is -// created against the current repository (working directory) - -// DiffOption ... +// DiffOption provides a way for setting specific options during a diff +// operation. Each supported option can customize the way the diff is +// executed against the current repository (working directory) type DiffOption func(*diffOptions) type diffOptions struct { DiffPaths []string } -// WithDiffPaths ... +// WithDiffPaths allows the diff to be targetted to specific files and +// folers within the current repository (working directory). Paths to +// files and folders are relative to the root of the repository. All +// leading and trailing whitepsace will be trimmed from the file paths, +// allowing empty paths to be ignored func WithDiffPaths(paths ...string) DiffOption { return func(opts *diffOptions) { opts.DiffPaths = trim(paths...) } } -// FileDiff ... +// FileDiff represents a snapshot containing all of the changes to +// a file within a repository (working directory) type FileDiff struct { - Path string + // Path of the file within the repository (working directory) + Path string + + // DiffChunk contains all of the identified changes within + // the file Chunks []DiffChunk } -// DiffChunk ... +// DiffChunk represents a snapshot of a single change (chunk) to +// a file within a repository (working directory) type DiffChunk struct { - Added DiffChange + // Added optionally contains details of the text that has + // been added to a file as part of the current change + Added DiffChange + + // Removed optionally contains details of the text that has + // been removed from a file as part of the current change Removed DiffChange } -// DiffChange ... +// DiffChange captures details about an individual chunk +// within a git diff. It contains both the changed text and +// its exact position (and line count) within the file type DiffChange struct { + // LineNo is the position within the file where the + // change starts LineNo int - Count int + + // Count is the number of lines that has changed + Count int + + // Change contains the text that has changed Change string } -// Diff ... +// Diff captures the changes made to files within the current repository (working +// directory). Options can be provided to customize how the current diff is +// determined. By default, all diffs (or changes) to files within the repository +// will be retrieved. The diff is generated using the following git options: +// +// git diff -U0 --no-color func (c *Client) Diff(opts ...DiffOption) ([]FileDiff, error) { options := &diffOptions{} for _, opt := range opts { diff --git a/diff_test.go b/diff_test.go index 6757c42..2847d3d 100644 --- a/diff_test.go +++ b/diff_test.go @@ -66,3 +66,18 @@ func print() { assert.Equal(t, 1, diffs[0].Chunks[1].Removed.Count) assert.Equal(t, ` print()`, diffs[0].Chunks[1].Removed.Change) } + +func TestDiffWithDiffPaths(t *testing.T) { + gittest.InitRepository(t, + gittest.WithCommittedFiles("file1.txt", "file2.txt"), + gittest.WithFileContent("file1.txt", "Hello, World!", "file2.txt", "Goodbye, World!")) + + overwriteFile(t, "file1.txt", "Goodbye, World!") + overwriteFile(t, "file2.txt", "Hello, World!") + + client, _ := git.NewClient() + diffs, err := client.Diff(git.WithDiffPaths("file1.txt")) + require.NoError(t, err) + + assert.Len(t, diffs, 1) +} From 7a52f172925afeb788d8fe45a962f80b768b0fcc Mon Sep 17 00:00:00 2001 From: purpleclay Date: Sun, 18 Aug 2024 07:01:40 +0100 Subject: [PATCH 3/3] support git diffs --- diff.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.go b/diff.go index 038e0c3..28c1e68 100644 --- a/diff.go +++ b/diff.go @@ -27,7 +27,7 @@ type diffOptions struct { DiffPaths []string } -// WithDiffPaths allows the diff to be targetted to specific files and +// WithDiffPaths allows the diff to be targeted to specific files and // folers within the current repository (working directory). Paths to // files and folders are relative to the root of the repository. All // leading and trailing whitepsace will be trimmed from the file paths,