From 9bc754992807c688022cda122fbd336d2ed8685c Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Sun, 8 May 2016 22:57:52 -0700 Subject: [PATCH] Fix support for empty diffs (new, deleted, renamed, binary files). There are situations where a diff is empty. For example, if a blank file is added a removed. Or if a file is renamed without any changes. This change detects and handles those situations properly. That includes fixing support for modified binary files. Improve test coverage significantly. Fixes #10. --- diff/diff.proto | 4 +- diff/diff_test.go | 278 +++++++++++++++++- diff/parse.go | 60 +++- diff/print.go | 16 +- diff/testdata/empty_multi.diff | 2 - .../sample_file_extended_empty_binary.diff | 3 + .../sample_file_extended_empty_deleted.diff | 3 + .../sample_file_extended_empty_new.diff | 3 + .../sample_file_extended_empty_rename.diff | 4 + diff/testdata/sample_multi_file_binary.diff | 29 ++ diff/testdata/sample_multi_file_deleted.diff | 27 ++ diff/testdata/sample_multi_file_new.diff | 27 ++ 12 files changed, 431 insertions(+), 25 deletions(-) create mode 100644 diff/testdata/sample_file_extended_empty_binary.diff create mode 100644 diff/testdata/sample_file_extended_empty_deleted.diff create mode 100644 diff/testdata/sample_file_extended_empty_new.diff create mode 100644 diff/testdata/sample_file_extended_empty_rename.diff create mode 100644 diff/testdata/sample_multi_file_binary.diff create mode 100644 diff/testdata/sample_multi_file_deleted.diff create mode 100644 diff/testdata/sample_multi_file_new.diff diff --git a/diff/diff.proto b/diff/diff.proto index 8915017..8868970 100644 --- a/diff/diff.proto +++ b/diff/diff.proto @@ -10,9 +10,9 @@ option (gogoproto.marshaler_all) = true; option (gogoproto.sizer_all) = true; // A FileDiff represents a unified diff for a single file. -// +// // A file unified diff has a header that resembles the following: -// +// // --- oldname 2009-10-11 15:12:20.000000000 -0700 // +++ newname 2009-10-11 15:12:30.000000000 -0700 message FileDiff { diff --git a/diff/diff_test.go b/diff/diff_test.go index fc7c8a0..4cd9f94 100644 --- a/diff/diff_test.go +++ b/diff/diff_test.go @@ -8,6 +8,9 @@ import ( "strings" "testing" "time" + + "github.com/shurcooL/go-goon" + "sourcegraph.com/sqs/pbtypes" ) func init() { @@ -91,6 +94,268 @@ func TestParseHunksAndPrintHunks(t *testing.T) { } } +func TestParseFileDiffHeaders(t *testing.T) { + tests := []struct { + filename string + wantDiff *FileDiff + }{ + { + filename: "sample_file.diff", + wantDiff: &FileDiff{ + OrigName: "oldname", + OrigTime: &pbtypes.Timestamp{Seconds: 1255273940}, + NewName: "newname", + NewTime: &pbtypes.Timestamp{Seconds: 1255273950}, + }, + }, + { + filename: "sample_file_extended.diff", + wantDiff: &FileDiff{ + OrigName: "oldname", + OrigTime: &pbtypes.Timestamp{Seconds: 1255273940}, + NewName: "newname", + NewTime: &pbtypes.Timestamp{Seconds: 1255273950}, + Extended: []string{ + "diff --git a/vcs/git_cmd.go b/vcs/git_cmd.go", + "index aa4de15..7c048ab 100644", + }, + }, + }, + { + filename: "sample_file_extended_empty_new.diff", + wantDiff: &FileDiff{ + OrigName: "/dev/null", + OrigTime: nil, + NewName: "b/vendor/go/build/testdata/empty/dummy", + NewTime: nil, + Extended: []string{ + "diff --git a/vendor/go/build/testdata/empty/dummy b/vendor/go/build/testdata/empty/dummy", + "new file mode 100644", + "index 0000000..e69de29", + }, + }, + }, + { + filename: "sample_file_extended_empty_deleted.diff", + wantDiff: &FileDiff{ + OrigName: "a/vendor/go/build/testdata/empty/dummy", + OrigTime: nil, + NewName: "/dev/null", + NewTime: nil, + Extended: []string{ + "diff --git a/vendor/go/build/testdata/empty/dummy b/vendor/go/build/testdata/empty/dummy", + "deleted file mode 100644", + "index e69de29..0000000", + }, + }, + }, + { + filename: "sample_file_extended_empty_rename.diff", + wantDiff: &FileDiff{ + OrigName: "a/docs/integrations/Email_Notifications.md", + OrigTime: nil, + NewName: "b/docs/integrations/email-notifications.md", + NewTime: nil, + Extended: []string{ + "diff --git a/docs/integrations/Email_Notifications.md b/docs/integrations/email-notifications.md", + "similarity index 100%", + "rename from docs/integrations/Email_Notifications.md", + "rename to docs/integrations/email-notifications.md", + }, + }, + }, + } + for _, test := range tests { + diffData, err := ioutil.ReadFile(filepath.Join("testdata", test.filename)) + if err != nil { + t.Fatal(err) + } + diff, err := ParseFileDiff(diffData) + if err != nil { + t.Fatalf("%s: got ParseFileDiff error %v", test.filename, err) + } + + diff.Hunks = nil + if got, want := diff, test.wantDiff; !reflect.DeepEqual(got, want) { + t.Errorf("%s:\n\ngot: %v\nwant: %v", test.filename, goon.Sdump(got), goon.Sdump(want)) + } + } +} + +func TestParseMultiFileDiffHeaders(t *testing.T) { + tests := []struct { + filename string + wantDiffs []*FileDiff + }{ + { + filename: "sample_multi_file_new.diff", + wantDiffs: []*FileDiff{ + { + OrigName: "/dev/null", + OrigTime: nil, + NewName: "b/_vendor/go/build/syslist_test.go", + NewTime: nil, + Extended: []string{ + "diff --git a/_vendor/go/build/syslist_test.go b/_vendor/go/build/syslist_test.go", + "new file mode 100644", + "index 0000000..3be2928", + }, + }, + { + OrigName: "/dev/null", + OrigTime: nil, + NewName: "b/_vendor/go/build/testdata/empty/dummy", + NewTime: nil, + Extended: []string{ + "diff --git a/_vendor/go/build/testdata/empty/dummy b/_vendor/go/build/testdata/empty/dummy", + "new file mode 100644", + "index 0000000..e69de29", + }, + }, + { + OrigName: "/dev/null", + OrigTime: nil, + NewName: "b/_vendor/go/build/testdata/multi/file.go", + NewTime: nil, + Extended: []string{ + "diff --git a/_vendor/go/build/testdata/multi/file.go b/_vendor/go/build/testdata/multi/file.go", + "new file mode 100644", + "index 0000000..ee946eb", + }, + }, + }, + }, + { + filename: "sample_multi_file_deleted.diff", + wantDiffs: []*FileDiff{ + { + OrigName: "a/vendor/go/build/syslist_test.go", + OrigTime: nil, + NewName: "/dev/null", + NewTime: nil, + Extended: []string{ + "diff --git a/vendor/go/build/syslist_test.go b/vendor/go/build/syslist_test.go", + "deleted file mode 100644", + "index 3be2928..0000000", + }, + }, + { + OrigName: "a/vendor/go/build/testdata/empty/dummy", + OrigTime: nil, + NewName: "/dev/null", + NewTime: nil, + Extended: []string{ + "diff --git a/vendor/go/build/testdata/empty/dummy b/vendor/go/build/testdata/empty/dummy", + "deleted file mode 100644", + "index e69de29..0000000", + }, + }, + { + OrigName: "a/vendor/go/build/testdata/multi/file.go", + OrigTime: nil, + NewName: "/dev/null", + NewTime: nil, + Extended: []string{ + "diff --git a/vendor/go/build/testdata/multi/file.go b/vendor/go/build/testdata/multi/file.go", + "deleted file mode 100644", + "index ee946eb..0000000", + }, + }, + }, + }, + { + filename: "sample_multi_file_rename.diff", + wantDiffs: []*FileDiff{ + { + OrigName: "a/README.md", + OrigTime: nil, + NewName: "b/README.md", + NewTime: nil, + Extended: []string{ + "diff --git a/README.md b/README.md", + "index 5f3d591..96a24fa 100644", + }, + }, + { + OrigName: "a/docs/integrations/Email_Notifications.md", + OrigTime: nil, + NewName: "b/docs/integrations/email-notifications.md", + NewTime: nil, + Extended: []string{ + "diff --git a/docs/integrations/Email_Notifications.md b/docs/integrations/email-notifications.md", + "similarity index 100%", + "rename from docs/integrations/Email_Notifications.md", + "rename to docs/integrations/email-notifications.md", + }, + }, + { + OrigName: "a/release_notes.md", + OrigTime: nil, + NewName: "b/release_notes.md", + NewTime: nil, + Extended: []string{ + "diff --git a/release_notes.md b/release_notes.md", + "index f2ff13f..f060cb5 100644", + }, + }, + }, + }, + { + filename: "sample_multi_file_binary.diff", + wantDiffs: []*FileDiff{ + { + OrigName: "a/README.md", + OrigTime: nil, + NewName: "b/README.md", + NewTime: nil, + Extended: []string{ + "diff --git a/README.md b/README.md", + "index 7b73e04..36cde13 100644", + }, + }, + { + OrigName: "a/data/Font.png", + OrigTime: nil, + NewName: "b/data/Font.png", + NewTime: nil, + Extended: []string{ + "diff --git a/data/Font.png b/data/Font.png", + "index 17a971d..599f8dd 100644", + "Binary files a/data/Font.png and b/data/Font.png differ", + }, + }, + { + OrigName: "a/main.go", + OrigTime: nil, + NewName: "b/main.go", + NewTime: nil, + Extended: []string{ + "diff --git a/main.go b/main.go", + "index 1aced1e..98a982e 100644", + }, + }, + }, + }, + } + for _, test := range tests { + diffData, err := ioutil.ReadFile(filepath.Join("testdata", test.filename)) + if err != nil { + t.Fatal(err) + } + diffs, err := ParseMultiFileDiff(diffData) + if err != nil { + t.Fatalf("%s: got ParseMultiFileDiff error %v", test.filename, err) + } + + for i := range diffs { + diffs[i].Hunks = nil // This test focuses on things other than hunks, so don't compare them. + } + if got, want := diffs, test.wantDiffs; !reflect.DeepEqual(got, want) { + t.Errorf("%s:\n\ngot: %v\nwant: %v", test.filename, goon.Sdump(got), goon.Sdump(want)) + } + } +} + func TestParseFileDiffAndPrintFileDiff(t *testing.T) { tests := []struct { filename string @@ -99,6 +364,10 @@ func TestParseFileDiffAndPrintFileDiff(t *testing.T) { {filename: "sample_file.diff"}, {filename: "sample_file_no_timestamp.diff"}, {filename: "sample_file_extended.diff"}, + {filename: "sample_file_extended_empty_new.diff"}, + {filename: "sample_file_extended_empty_deleted.diff"}, + {filename: "sample_file_extended_empty_rename.diff"}, + {filename: "sample_file_extended_empty_binary.diff"}, { filename: "empty.diff", wantParseErr: &ParseError{0, 0, ErrExtendedHeadersEOF}, @@ -136,7 +405,10 @@ func TestParseMultiFileDiffAndPrintMultiFileDiff(t *testing.T) { }{ {filename: "sample_multi_file.diff", wantFileDiffs: 2}, {filename: "sample_multi_file_single.diff", wantFileDiffs: 1}, + {filename: "sample_multi_file_new.diff", wantFileDiffs: 3}, + {filename: "sample_multi_file_deleted.diff", wantFileDiffs: 3}, {filename: "sample_multi_file_rename.diff", wantFileDiffs: 3}, + {filename: "sample_multi_file_binary.diff", wantFileDiffs: 3}, {filename: "long_line_multi.diff", wantFileDiffs: 3}, {filename: "empty.diff", wantFileDiffs: 0}, {filename: "empty_multi.diff", wantFileDiffs: 2}, @@ -146,7 +418,7 @@ func TestParseMultiFileDiffAndPrintMultiFileDiff(t *testing.T) { if err != nil { t.Fatal(err) } - diff, err := ParseMultiFileDiff(diffData) + diffs, err := ParseMultiFileDiff(diffData) if err != test.wantParseErr { t.Errorf("%s: got ParseMultiFileDiff err %v, want %v", test.filename, err, test.wantParseErr) continue @@ -155,11 +427,11 @@ func TestParseMultiFileDiffAndPrintMultiFileDiff(t *testing.T) { continue } - if got, want := len(diff), test.wantFileDiffs; got != want { + if got, want := len(diffs), test.wantFileDiffs; got != want { t.Errorf("%s: got %v instances of diff.FileDiff, expected %v", test.filename, got, want) } - printed, err := PrintMultiFileDiff(diff) + printed, err := PrintMultiFileDiff(diffs) if err != nil { t.Errorf("%s: PrintMultiFileDiff: %s", test.filename, err) } diff --git a/diff/parse.go b/diff/parse.go index 65baaa5..fb14f33 100644 --- a/diff/parse.go +++ b/diff/parse.go @@ -53,7 +53,7 @@ func (r *MultiFileDiffReader) ReadFile() (*FileDiff, error) { } r.nextFileFirstLine = nil - d, err := fr.ReadAllHeaders() + fd, err := fr.ReadAllHeaders() if err != nil { switch e := err.(type) { case *ParseError: @@ -63,7 +63,7 @@ func (r *MultiFileDiffReader) ReadFile() (*FileDiff, error) { case OverflowError: r.nextFileFirstLine = []byte(e) - return d, nil + return fd, nil default: return nil, err @@ -79,12 +79,12 @@ func (r *MultiFileDiffReader) ReadFile() (*FileDiff, error) { hr := fr.HunksReader() line, err := readLine(r.reader) if err != nil { - return d, err + return fd, err } line = bytes.TrimSuffix(line, []byte{'\n'}) if bytes.HasPrefix(line, hunkPrefix) { hr.nextHunkHeaderLine = line - d.Hunks, err = hr.ReadAllHunks() + fd.Hunks, err = hr.ReadAllHunks() r.line = fr.line r.offset = fr.offset if err != nil { @@ -93,7 +93,7 @@ func (r *MultiFileDiffReader) ReadFile() (*FileDiff, error) { // This just means we finished reading the hunks for the // current file. See the ErrBadHunkLine doc for more info. r.nextFileFirstLine = e.Line - return d, nil + return fd, nil } } return nil, err @@ -104,7 +104,7 @@ func (r *MultiFileDiffReader) ReadFile() (*FileDiff, error) { r.nextFileFirstLine = line } - return d, nil + return fd, nil } // ReadAllFiles reads all file unified diffs (including headers and all @@ -154,17 +154,17 @@ type FileDiffReader struct { // Read reads a file unified diff, including headers and hunks, from r. func (r *FileDiffReader) Read() (*FileDiff, error) { - d, err := r.ReadAllHeaders() + fd, err := r.ReadAllHeaders() if err != nil { return nil, err } - d.Hunks, err = r.HunksReader().ReadAllHunks() + fd.Hunks, err = r.HunksReader().ReadAllHunks() if err != nil { return nil, err } - return d, nil + return fd, nil } // ReadAllHeaders reads the file headers and extended headers (if any) @@ -177,7 +177,16 @@ func (r *FileDiffReader) ReadAllHeaders() (*FileDiff, error) { fd := &FileDiff{} fd.Extended, err = r.ReadExtendedHeaders() - if err != nil { + if pe, ok := err.(*ParseError); ok && pe.Err == ErrExtendedHeadersEOF { + wasEmpty := handleEmpty(fd) + if wasEmpty { + return fd, nil + } + return fd, err + } else if _, ok := err.(OverflowError); ok { + handleEmpty(fd) + return fd, err + } else if err != nil { return fd, err } @@ -297,7 +306,7 @@ func (r *FileDiffReader) ReadExtendedHeaders() ([]string, error) { r.fileHeaderLine = nil } - if bytes.HasPrefix(line, []byte("diff --git")) { + if bytes.HasPrefix(line, []byte("diff --git ")) { if firstLine { firstLine = false } else { @@ -316,6 +325,35 @@ func (r *FileDiffReader) ReadExtendedHeaders() ([]string, error) { } } +// handleEmpty detects when FileDiff was an empty diff and will not have any hunks +// that follow. It updates fd fields from the parsed extended headers. +func handleEmpty(fd *FileDiff) (wasEmpty bool) { + switch { + case len(fd.Extended) == 3 && strings.HasPrefix(fd.Extended[1], "new file mode ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = "/dev/null" + fd.NewName = names[1] + return true + case len(fd.Extended) == 3 && strings.HasPrefix(fd.Extended[1], "deleted file mode ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = names[0] + fd.NewName = "/dev/null" + return true + case len(fd.Extended) == 4 && strings.HasPrefix(fd.Extended[2], "rename from ") && strings.HasPrefix(fd.Extended[3], "rename to ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = names[0] + fd.NewName = names[1] + return true + case len(fd.Extended) == 3 && strings.HasPrefix(fd.Extended[2], "Binary files ") && strings.HasPrefix(fd.Extended[0], "diff --git "): + names := strings.SplitN(fd.Extended[0][len("diff --git "):], " ", 2) + fd.OrigName = names[0] + fd.NewName = names[1] + return true + default: + return false + } +} + var ( // ErrNoFileHeader is when a file unified diff has no file header // (i.e., the lines that begin with "---" and "+++"). diff --git a/diff/print.go b/diff/print.go index 9cca4fa..801d2ce 100644 --- a/diff/print.go +++ b/diff/print.go @@ -36,13 +36,15 @@ func PrintFileDiff(d *FileDiff) ([]byte, error) { } } - if d.OrigName != "" || d.OrigTime != nil || d.NewName != "" || d.NewTime != nil { - if err := printFileHeader(&buf, "--- ", d.OrigName, timePtr(d.OrigTime)); err != nil { - return nil, err - } - if err := printFileHeader(&buf, "+++ ", d.NewName, timePtr(d.NewTime)); err != nil { - return nil, err - } + if d.Hunks == nil { + return buf.Bytes(), nil + } + + if err := printFileHeader(&buf, "--- ", d.OrigName, timePtr(d.OrigTime)); err != nil { + return nil, err + } + if err := printFileHeader(&buf, "+++ ", d.NewName, timePtr(d.NewTime)); err != nil { + return nil, err } ph, err := PrintHunks(d.Hunks) diff --git a/diff/testdata/empty_multi.diff b/diff/testdata/empty_multi.diff index be21294..b85b802 100644 --- a/diff/testdata/empty_multi.diff +++ b/diff/testdata/empty_multi.diff @@ -1,8 +1,6 @@ diff --git Godeps/_workspace/src/sourcegraph.com/sourcegraph/go-diff/diff/testdata/empty.diff Godeps/_workspace/src/sourcegraph.com/sourcegraph/go-diff/diff/testdata/empty.diff new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 ---- /dev/null -+++ Godeps/_workspace/src/sourcegraph.com/sourcegraph/go-diff/diff/testdata/empty.diff diff --git Godeps/_workspace/src/sourcegraph.com/sourcegraph/go-diff/diff/testdata/empty_new.diff Godeps/_workspace/src/sourcegraph.com/sourcegraph/go-diff/diff/testdata/empty_new.diff new file mode 100644 index 0000000000000000000000000000000000000000..527e2e70f57b02e709f53e3ac2b7f59e2b5a46bc diff --git a/diff/testdata/sample_file_extended_empty_binary.diff b/diff/testdata/sample_file_extended_empty_binary.diff new file mode 100644 index 0000000..6ed9fa1 --- /dev/null +++ b/diff/testdata/sample_file_extended_empty_binary.diff @@ -0,0 +1,3 @@ +diff --git a/data/Font.png b/data/Font.png +index 17a971d..599f8dd 100644 +Binary files a/data/Font.png and b/data/Font.png differ diff --git a/diff/testdata/sample_file_extended_empty_deleted.diff b/diff/testdata/sample_file_extended_empty_deleted.diff new file mode 100644 index 0000000..27e33cf --- /dev/null +++ b/diff/testdata/sample_file_extended_empty_deleted.diff @@ -0,0 +1,3 @@ +diff --git a/vendor/go/build/testdata/empty/dummy b/vendor/go/build/testdata/empty/dummy +deleted file mode 100644 +index e69de29..0000000 diff --git a/diff/testdata/sample_file_extended_empty_new.diff b/diff/testdata/sample_file_extended_empty_new.diff new file mode 100644 index 0000000..36e4605 --- /dev/null +++ b/diff/testdata/sample_file_extended_empty_new.diff @@ -0,0 +1,3 @@ +diff --git a/vendor/go/build/testdata/empty/dummy b/vendor/go/build/testdata/empty/dummy +new file mode 100644 +index 0000000..e69de29 diff --git a/diff/testdata/sample_file_extended_empty_rename.diff b/diff/testdata/sample_file_extended_empty_rename.diff new file mode 100644 index 0000000..9ed0a52 --- /dev/null +++ b/diff/testdata/sample_file_extended_empty_rename.diff @@ -0,0 +1,4 @@ +diff --git a/docs/integrations/Email_Notifications.md b/docs/integrations/email-notifications.md +similarity index 100% +rename from docs/integrations/Email_Notifications.md +rename to docs/integrations/email-notifications.md diff --git a/diff/testdata/sample_multi_file_binary.diff b/diff/testdata/sample_multi_file_binary.diff new file mode 100644 index 0000000..66fbc06 --- /dev/null +++ b/diff/testdata/sample_multi_file_binary.diff @@ -0,0 +1,29 @@ +diff --git a/README.md b/README.md +index 7b73e04..36cde13 100644 +--- a/README.md ++++ b/README.md +@@ -1,6 +1,8 @@ + Conception-go [![Build Status](https://travis-ci.org/shurcooL/Conception-go.svg?branch=master)](https://travis-ci.org/shurcooL/Conception-go) + ============= + ++This is a change. ++ + This is a work in progress Go implementation of [Conception](https://github.com/shurcooL/Conception#demonstration). + + Conception is an experimental project. It's a platform for researching software development tools and techniques. It is driven by a set of guiding principles. Conception-go targets Go development. +diff --git a/data/Font.png b/data/Font.png +index 17a971d..599f8dd 100644 +Binary files a/data/Font.png and b/data/Font.png differ +diff --git a/main.go b/main.go +index 1aced1e..98a982e 100644 +--- a/main.go ++++ b/main.go +@@ -6710,6 +6710,8 @@ func init() { + } + + func main() { ++ // Another plain text change. ++ + //defer profile.Start(profile.CPUProfile).Stop() + //defer profile.Start(profile.MemProfile).Stop() + diff --git a/diff/testdata/sample_multi_file_deleted.diff b/diff/testdata/sample_multi_file_deleted.diff new file mode 100644 index 0000000..eead972 --- /dev/null +++ b/diff/testdata/sample_multi_file_deleted.diff @@ -0,0 +1,27 @@ +diff --git a/vendor/go/build/syslist_test.go b/vendor/go/build/syslist_test.go +deleted file mode 100644 +index 3be2928..0000000 +--- a/vendor/go/build/syslist_test.go ++++ /dev/null +@@ -1,62 +0,0 @@ +-func TestGoodOSArch(t *testing.T) { +- for _, test := range tests { +- if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result { +- t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result) +- } +- } +-} +diff --git a/vendor/go/build/testdata/empty/dummy b/vendor/go/build/testdata/empty/dummy +deleted file mode 100644 +index e69de29..0000000 +diff --git a/vendor/go/build/testdata/multi/file.go b/vendor/go/build/testdata/multi/file.go +deleted file mode 100644 +index ee946eb..0000000 +--- a/vendor/go/build/testdata/multi/file.go ++++ /dev/null +@@ -1,5 +0,0 @@ +-// Test data - not compiled. +- +-package main +- +-func main() {} diff --git a/diff/testdata/sample_multi_file_new.diff b/diff/testdata/sample_multi_file_new.diff new file mode 100644 index 0000000..b5e1788 --- /dev/null +++ b/diff/testdata/sample_multi_file_new.diff @@ -0,0 +1,27 @@ +diff --git a/_vendor/go/build/syslist_test.go b/_vendor/go/build/syslist_test.go +new file mode 100644 +index 0000000..3be2928 +--- /dev/null ++++ b/_vendor/go/build/syslist_test.go +@@ -0,0 +1,62 @@ ++func TestGoodOSArch(t *testing.T) { ++ for _, test := range tests { ++ if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result { ++ t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result) ++ } ++ } ++} +diff --git a/_vendor/go/build/testdata/empty/dummy b/_vendor/go/build/testdata/empty/dummy +new file mode 100644 +index 0000000..e69de29 +diff --git a/_vendor/go/build/testdata/multi/file.go b/_vendor/go/build/testdata/multi/file.go +new file mode 100644 +index 0000000..ee946eb +--- /dev/null ++++ b/_vendor/go/build/testdata/multi/file.go +@@ -0,0 +1,5 @@ ++// Test data - not compiled. ++ ++package main ++ ++func main() {}