-
Notifications
You must be signed in to change notification settings - Fork 534
format/diff: unified diff encoder and public API #388
Conversation
chunkInit = "@@ -%s +%s @@%s\n" | ||
chunkCount = "%d,%d" | ||
|
||
noFilePath = "/dev/null" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about Windows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the way that unified diff uses to represent "no file". Is OS independent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, in git it's hardcoded without any conditional.
@@ -42,6 +43,17 @@ func (f *File) Contents() (content string, err error) { | |||
return buf.String(), nil | |||
} | |||
|
|||
// IsBinary returns if the file is binary or not | |||
func (f *File) IsBinary() (bool, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lacks of test on this package
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
Codecov Report
@@ Coverage Diff @@
## master #388 +/- ##
==========================================
- Coverage 77.27% 77.17% -0.11%
==========================================
Files 123 125 +2
Lines 8810 9085 +275
==========================================
+ Hits 6808 7011 +203
- Misses 1234 1311 +77
+ Partials 768 763 -5
Continue to review full report at Codecov.
|
var ErrBothFilesEmpty = errors.New("both files are empty") | ||
|
||
// UnifiedEncoder encodes an unified diff into the provided Writer. There are some unsupported features: | ||
// - Similarity index is not supported |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Similarity index/Similarity index for renames/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to add is not supported
if you just said this is a list of unsupported features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
} | ||
|
||
for _, p := range patch.FilePatches() { | ||
f, t := p.Files() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
encodeFilePatch
function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
func (e *UnifiedEncoder) printMessage(message string) error { | ||
isEmpty := message == "" | ||
hasSuffix := strings.HasSuffix(message, "\n") | ||
msg := message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to declare this variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
|
||
func (c *hunksGenerator) Generate() []*hunk { | ||
for i, chunk := range c.chunks { | ||
ls := splitLines(chunk.Content()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
separate function?
c.beforeContext = nil | ||
} | ||
|
||
// Logic used to obtain line numbers in a new chunk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggested wording: addLineNumbers obtains the line numbers in a new chunk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
plumbing/object/commit.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
toTree, err := to.Tree() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
space
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
plumbing/object/patch.go
Outdated
func getPatch(message string, changes ...*Change) (fdiff.Patcheable, error) { | ||
var filePatches []fdiff.FilePatch | ||
for _, c := range changes { | ||
from, to, err := c.Files() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
utils/binary/read.go
Outdated
|
||
const sniffLen = 8000 | ||
|
||
//IsBinary detects if data is a binary value based on: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spaces after //
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the package can be improved by writing a package level documentation explaining the package itself, its purpose, design and style. Some examples will be good too.
The tests are quite awesome.
I don't know much about templates, but it looks as if the unified encoder can be done using templates. Not sure if it is a good idea though.
The explanation of the encoder should come along with a pointer to the documentation of the format.
plumbing/format/diff/patch.go
Outdated
"gopkg.in/src-d/go-git.v4/plumbing/filemode" | ||
) | ||
|
||
type Operation int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document this type and its values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
plumbing/format/diff/patch.go
Outdated
|
||
// Patch represents the necessary steps to transform several files to another. | ||
type Patch interface { | ||
FilePatches() []FilePatch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document this methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
plumbing/format/diff/patch.go
Outdated
|
||
// Patcheable is a Patch but with several ways to encode it. It is used to be | ||
// able to use Patch in a fluent way. | ||
type Patcheable interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this interface or its comments.
I think the name, Patcheable, is not transmitting the full meaning of the interface, as implementing this interface does not mean something can be Pathed.
Also, the comment states the are several ways to encode it, but there is only one method and it is not called encode or mention encoding anywhere in its documentation.
Maybe if you explain what you want to achieve here and how, it will be easier to understand. And example can also help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrote the documentation, maybe is clear now. If not, can you suggest a better name for this interface?
plumbing/format/diff/patch.go
Outdated
// UnifiedDiff writes the patch into the provided writer in unified | ||
// diff format. ctxLines changes the amount of unchanged lines that will be | ||
// printed before and after each chunk. | ||
UnifiedDiff(ctxLines int, w io.Writer) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the writer the first argument, a l`a golang way.
I don't understand this, shouldn't this be an encoder of Path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the documentation, I hope is clear now.
✅ to change arguments order.
plumbing/format/diff/patch.go
Outdated
|
||
// FilePatch represents the necessary steps to transform one file to another | ||
type FilePatch interface { | ||
IsBinary() bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document the methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is binary here, to or from or the FilePath itself, if it is the FilePath itself, then explain that there are two types of FilePatches and why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explain what will from and to be when inserting or deleting a file (Hash?, filemode? path? or it will be just nil?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
} | ||
|
||
func (e *UnifiedEncoder) prints(s string) error { | ||
_, err := e.Write([]byte(s)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this all together, it is a print method that does not print.
This should be a call to bytes.Buffer.WriteTo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
} | ||
} | ||
|
||
func (c *hunksGenerator) Generate() []*hunk { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the struct is private, to know which methods are intended to be used outside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is usually achieved in Go using an internal package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't need to be an internal package, this is also a good practice at least for me
return buf.String() | ||
} | ||
|
||
func (c *hunk) AddOp(t Operation, s ...string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the struct is private, to know which methods are intended to be used outside.
plumbing/object/change.go
Outdated
@@ -78,6 +79,12 @@ func (c *Change) String() string { | |||
return fmt.Sprintf("<Action: %s, Path: %s>", action, c.name()) | |||
} | |||
|
|||
// Patch returns a Patch with all the file changes in chunks. This | |||
// representation can be used to create several diff outputs. | |||
func (c *Change) Patch() (diff.Patcheable, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will it make sense for Change to implement Patch or FilePatch instead of having a method that returns a Path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plumbing/object/patch.go
Outdated
filePatches []fdiff.FilePatch | ||
} | ||
|
||
func (t textPatch) FilePatches() []fdiff.FilePatch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document all the methods in these files. A simple ... implements <interface>.
will suffice most of the times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
plumbing/format/diff/patch.go
Outdated
Delete | ||
) | ||
|
||
// Patch represents the necessary steps to transform several files to another. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
English can be improved a little bit by simplifying the statement:
Patch represents a collection of steps to transform several files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🆗
plumbing/format/diff/patch.go
Outdated
// Patcheable contains ways to represent a patch. Its use is intended to create | ||
// fluents APIs, and be able to discover more easily ways to work with Patches. | ||
// You don't need to know that a NewUnifiedEncoder constructor exists, you just | ||
// need to see the available methods after get a Patch. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry, I still don't understand this interface. It looks as a Patch encoder but the name and the description suggest otherwise. Maybe it is just me, let's talk about this. Also the name of the function is specific to a format, but that format is not part of the name of the interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea of this interface is do this:
p, err := commit.Patch(otherCommit)
handleErr(err)
err = p.UnifiedDiff(buf,2)
handleErr(err)
Instead of:
p, err := commit.Patch(otherCommit)
handleErr(err)
// To do this, you need to know the internals or find it into the documentation
ue := diff.NewUnifiedEncoder(buf, 1)
err = ue.Encode(p)
handleErr(err)
plumbing/format/diff/patch.go
Outdated
|
||
// Chunk is a operation to process to transform a file to another. | ||
// Operations can be Equal, Add or Delete | ||
type Chunk interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
func (e *UnifiedEncoder) printMessage(message string) { | ||
isEmpty := message == "" | ||
hasSuffix := strings.HasSuffix(message, "\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think endsInNewLine
is a better name for this variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the message is a specific part of the patch format, is the very beginning of the result file. This method is not intended to write other kind of lines, just this header message if any.
} | ||
|
||
func (c *hunk) WriteTo(buf *bytes.Buffer) { | ||
fromString := fmt.Sprintf(chunkCount, c.fromLine, c.fromCount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method need some improvements:
- don't use strings, write to buf directly using fmt.Fprintf
- don't generate the string / write to buf twice depending on c.fromCount or c.toCount, use an if ... else clause to generate the content only once.
toString = fmt.Sprintf("%d", c.toLine) | ||
} | ||
|
||
buf.WriteString(fmt.Sprintf(chunkInit, fromString, toString, c.ctxPrefix)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very wrong, just write to the buf with out generating the string.
} | ||
} | ||
|
||
func (c *hunksGenerator) Generate() []*hunk { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is usually achieved in Go using an internal package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version is super good. It still have some minor issues and I still don't understand the Patcheable interface, maybe we should talk about it.
@ajnavarro before merge please update https://github.com/src-d/go-git/blob/master/COMPATIBILITY.md |
- Added Patch interface - Added a Unified Diff encoder from Patches - Added Change method to generate Patches - Added Changes method to generate Patches - Added Tree method to generate Patches - Added Commit method to generate Patches
@mcuadros rebased and unnecessary commits squashed. Also added diff line into compatibility file. |
It resolves #57