121 Commits

Author SHA1 Message Date
Billy Keyes 265161e7a0 Checkpoint go-fuzz testing
I'm not sure yet if this should remain committed in the repository, but
need to checkpoint progress so far so I can resume on another computer.
2021-09-11 16:54:31 -04:00
Billy Keyes b6a90110a3 Fix out-of-bounds panic parsing timestamps (#28)
Found by go-fuzz.
2021-09-09 14:24:16 -04:00
Billy Keyes 53bcdf7e5d Fix EOF error for some files without final newline (#27)
If a file was an exact multiple of 1024 bytes (the size of an internal
buffer) and was missing a final newline, the LineReaderAt implementation
would drop the last line, leading to an unexpected EOF error on apply.

In addition to fixing the bug, slightly change the behavior of
ReadLineAt to reflect how it is actually used:

  1. Clarify that the return value n includes all lines instead of only
     lines with a final newline. This was already true except in the
     case of the bug fixed by this commit.

  2. Only return io.EOF if fewer lines are read than requested. The
     previous implementation also returned io.EOF if the last line was
     missing a final newline, but this was confusing and didn't really
     serve a purpose.

This is technically a breaking change for external implementations but
an implementation that exactly followed the "spec" was already broken in
certain edge cases.
v0.6.0
2021-08-19 17:32:18 -07:00
Javier Campanini b5756546a1 Decode quoted-printable UTF8 in email subjects (#25) v0.5.2 2021-07-19 11:45:58 -07:00
Billy Keyes 3772c9eb65 Fix parsing for file names with spaces (#24)
Git does not quote file names containing spaces when generating header
lines. However, I misread the Git implementation and thought it used
spaces as a name terminator by default, implying that names with spaces
would be quoted.

Rewrite the the name parsing code to better match Git. Specifically,
when both names in a header are not quoted, test all splits to see if
any of them produce two equal names. Also account for spaces in file
names when parsing file metadata.

Finally, allow trailing spaces on file names, which seem to be allowed
by Git (although this sounds like a terrible idea to me.)
v0.5.1
2021-04-24 15:23:31 -07:00
Billy Keyes b6666095cf Remove deprecated actions commands (#22) 2021-03-19 14:47:06 -07:00
Billy Keyes 8cd57be485 Update development status in README 2021-02-19 13:09:28 -08:00
George Dunlap b846b2cee0 Relax email header requirements (#20)
`git am` will accept patches which aren't fully RFC-compliant, as long
as they start with `From` and have a `Subject` line. Relax
ParsePatchHeader() so that it will accept such patches as well.

Specifically, it will tolerate patches of the form:

    From: <foo@company.com>

In this case, the `Author` field will contain `foo@company.com
<foo@company.com>`.  Duplicate this behavior.

Signed-off-by: George Dunlap <george.dunlap@citrix.com>
Co-authored-by: George Dunlap <george.dunlap@citrix.com>
2020-10-31 16:00:50 -07:00
George Dunlap 1bd59c4f11 Remove "appendix" information from commit message (#19)
...when parsing emails, similar to `git am`.

Add a new field, `BodyAppendix` to PatchHeader.

Modify `scanMessageBody` to accept a boolean argument saying whether
to separate out the appendix or not.  Do this by keeping two string
builders, and having it switch to the appendix builder when it finds a
`---` line.

Handling the newlines at the end as expected requires moving things
around a bit.

First, we were trimming space from the line once to decide whether the
line was empty, and then trimming space again if we determined it
wasn't empty.  This only needs to be done once.

Then, do all the trimming (both of whitespace and the prefix) first,
before deciding what to do about the line.

Request BodyAppendix separately when parsing a mail, but not a commit
message.

Add some tests to verify that it works as expected.

Signed-off-by: George Dunlap <george.dunlap@citrix.com>
Co-authored-by: George Dunlap <george.dunlap@citrix.com>
v0.5.0
2020-10-23 20:39:43 -07:00
George Dunlap 379b893435 Remove email decorations from patch titles (#17)
Primarily to get rid of [PATCH] at the front, but while we're here
just be generally compatible with `git am`:

 * Remove `re` and variations
 * Remove whitespace
 * Remove anything in brackets

But only at the very beginning of the subject.

Store anything removed in this way in PatchHeader.SubjectPrefix.

Inspired by
https://github.com/git/git/blob/master/mailinfo.c:cleanup_subject()

Signed-off-by: George Dunlap <george.dunlap@citrix.com>
Co-authored-by: George Dunlap <george.dunlap@citrix.com>
2020-09-15 20:25:25 -07:00
Billy Keyes 864dd3fb4f Replace godoc.org badge with pkg.go.dev badge 2020-09-12 21:43:52 -07:00
Billy Keyes d3116e7b7e Make patch date parsing simpler and stricter (#15)
Removed the distinction between parsed and raw dates and simply return
an error for unsupported date formats. Arbitrary date support was
probably a premature optimization and would be better supported by a
method to register custom date parsing functions.

Simple time.Time fields make the structure easier to use and mean that
a zero value always indicates the patch did not include a date.
v0.4.0
2020-06-15 22:07:06 -07:00
Billy Keyes f3b83ad722 Allow unpadded days in patch headers (#14)
A closer look at the Git source shows that the rfc2822 and default
formats do not zero-pad days, so we need to accept single-digit days
when parsing. The single-digit pattern will also accept the padded
format, so there's no loss of functionality.

Change the parsing test to use single-digit values to emphasize when
padding is required and when it is not.
2020-06-10 21:34:00 -07:00
Billy Keyes 4fa9801742 Check for leftover content after a full delete (#9)
When applying a text fragment that deletes all content in the file, make
sure there is no content left in the source after using all the lines in
the fragment. If there is is extra content, it's a conflict: something
added more lines to the file after generating the patch.
2020-05-03 13:39:08 -07:00
Billy Keyes b5ae0bd443 Rename PatchHeader.Message to .Body (#7)
Git usually refers to the whole thing (title + body) as the "commit
message", so using different terms for the parts clarifies things. Also
add a new Message() function that returns the combined string.
2020-04-26 20:00:20 -07:00
Billy Keyes 546a186b39 Fix minor documentation issues 2020-04-18 22:39:42 -07:00
Billy Keyes 5609b2d456 Fix typo in comment v0.3.0 2020-04-18 22:23:12 -07:00
Billy Keyes f0da09b10f Add ParsePatchHeader and related types (#6)
This function parses patch headers (the preamble returned by the
existing Parse function) to extract information about the commit that
generated the patch. This is useful when patches are an interchange
format and this library is applying commits generated elsewhere.

Because of the variety of header formats, parsing is fairly lenient and
best-effort, although certain invalid input does cause errors.

This also extracts some test utilities from the apply tests for reuse.
2020-04-18 22:21:55 -07:00
Billy Keyes c2035adeda Add Apply convenience function to match Parse (#5) 2020-04-11 15:21:23 -07:00
Billy Keyes 34ea173066 Add apply example to README 2020-02-01 13:30:59 -08:00
Billy Keyes bedbab3f65 Use GitHub actions for testing and linting (#3)
Use golangci-lint to coordinate linting instead of installing tools
individually. The workflow installs the binary directly instead of using
one of the existing public actions.
2020-02-01 13:19:05 -08:00
Billy Keyes 09f3004fce Fix flushing when data is larger than the buffer (#2)
copyFrom and copyLinesFrom did not increment the start offset after
reading, so the same lines were copied over and over again.
v0.2.1
2020-02-01 10:58:43 -08:00
Billy Keyes bb5b8f69fd Update README.md v0.2.0 2020-01-27 22:55:33 -08:00
Billy Keyes c979654ba5 Update README with apply status 2020-01-27 22:42:13 -08:00
Billy Keyes 9fc14fddb0 Add tests for file-level application
Also refactor the apply tests to use a common type now that there are
three variants that do almost the same thing for each test.
2020-01-27 22:35:13 -08:00
Billy Keyes 19ec1a24b4 Improve detection of in-progress application
Applying a file now rejects future text fragment applies under the
assumption that all relevant fragments were part of the file.
2020-01-26 15:02:42 -08:00
Billy Keyes 0f3872a5a4 Fix panic with empty input in LineReaderAt 2020-01-26 13:37:09 -08:00
Billy Keyes fa88623500 Improve clarity of LineReaderAt implementation
Try to avoid confusion about whether variables refer to byte or line
counts/positions. It still isn't perfect, but I'm not sure how to
further clarify without being overly verbose.
2020-01-25 22:27:59 -08:00
Billy Keyes 206a57eb7a Add tests for LineReaderAt implementation
This adds minimal new coverage, since the apply tests already exercise
this, but it's complicated enough that dedicated tests will be helpful.
2020-01-25 22:26:21 -08:00
Billy Keyes 18058d1e15 Move apply methods to an Applier type
This removes the distinction between "strict" and "fuzzy" application
by allowing future methods on Applier that control settings. It also
avoids state tracking in the text fragment apply signature by moving it
into the Applier type.

While in practice, an Applier will be used once and discarded, the
capability is provided to reset it for multiple uses.
2020-01-25 17:01:43 -08:00
Billy Keyes 3d1274d16e Remove unused LineReader interface
This is no longer used for text application, so revert the parser back
to using StringReader.
2020-01-25 16:57:23 -08:00
Billy Keyes 0b7af3feaa Implement text application using LineReaderAt
This is functionally equivalent to the previous version (except for one
error case), but uses the new interface. I think the code is simpler
overall because it removes the line tracking.
2020-01-25 16:56:33 -08:00
Billy Keyes f3702115a6 Add LineReaderAt interface and implementation
This is a line-oriented parallel to io.ReaderAt, meant for text applies.
While the mapping isn't quite as clean as in the binary case, a text
apply still reads a fixed chunk of lines starting at a specific line
number and modifies them. This also allows a consistent interface for
strict and fuzzy applies.

The implementation wraps an io.ReaderAt and reads data in chunks,
indexing line boundaries as it goes. This is probably not the most
efficient way to implement this interface, but it works and allows file
application to take a consistent interface.
2020-01-25 16:56:32 -08:00
Billy Keyes 131a046908 Use io.ReaderAt to implement binary applies
This better matches the actual contract of the delta patch, which reads
fixed size chunks of the source files at arbitrary positions.
2020-01-25 16:48:07 -08:00
Billy Keyes a34334d6ef Add error tests for BinaryFragment#Apply
These cover some of the possible errors with delta fragments. Many of
the same tests could be implemented slightly easier by testing the
helper functions, but the infrastructure is already set up to test the
full function.

This is made easier by the bin.go CLI, which can parse and encode binary
patch data. Once parsed, fragments were manipulated with truncate and
dd, encoded, and placed in the patch files.
2020-01-16 22:43:56 -08:00
Billy Keyes 194589f001 Add test for large binary delta application
This ensures a default size is used for large copy instructions.
2020-01-16 21:52:41 -08:00
Billy Keyes c50036a466 Abstract common file loading in apply tests 2020-01-16 21:52:41 -08:00
Billy Keyes 79aa2a9a77 Add positive tests for BinaryFragment#Apply
Covers basic literal and delta application. Input files were created by
using dd and the 'conv=notrunc' option to modify sections of files
created from /dev/urandom. All file start with two null bytes to
convince Git they are binary.
2020-01-16 21:52:40 -08:00
Billy Keyes a1d21957e2 Implement binary fragment application
Currently untested, but based on code I validated against some generated
patches. Tests comming in a future commit.
2020-01-14 22:49:14 -08:00
Billy Keyes 32887c7120 Update to Go 1.13 in go.mod
Tests now use errors.Is, a Go 1.13 feature.
2020-01-13 22:14:17 -08:00
Billy Keyes eb5f3de78c Improve conflict error detection
Conflict errors are now represented by an exported type that is
compatible with errors.Is instead of using the method defined on
ApplyError. This makes the tests slightly cleaner and should be more
idiomatic for clients.
2020-01-12 22:09:08 -08:00
Billy Keyes 1b341e0693 Add error tests for TextFragment#ApplyStrict
These cover the errors that can happen with a single fragment and no
additional manipulation by the caller of ApplyStrict.
2020-01-12 22:09:08 -08:00
Billy Keyes 6a055aa600 Add exact line test for TextFragment#ApplyStrict
This ensures the line number is used and application isn't based only on
matching context lines.
2020-01-12 22:08:15 -08:00
Billy Keyes 774281f01b Standardize on int64 for line numbers
This matches the type used for positions in text fragments.
2020-01-09 22:11:25 -08:00
Billy Keyes 7bd4e0bb0e Fix EOF handling in TextFragment#ApplyStrict
io.EOF was not properly accounted for when dealing with patches that are
missing trailing newline characters.
2020-01-09 22:05:24 -08:00
Billy Keyes 02f7ff4f79 Add positive tests for TextFragment#ApplyStrict
These cover all of the cases (I think) where application should succeed.
2020-01-08 23:04:44 -08:00
Billy Keyes ae704236bb Return ApplyError when ApplyString fails
This wraps the underlying error with optional position information and
provides a way to test if the error was due to a conflict. At the
moment, details about the conflict are not exposed outside of the
message string.
2020-01-07 22:59:25 -08:00
Billy Keyes 93d46e805c Add initial pass at ApplyStrict functions
Text fragment application is implemented but untested and with several
TODOs, binary fragment application is unimplemented and will panic.
2020-01-05 20:37:59 -08:00
Billy Keyes 25bee8cf2f Add validation function for text fragment
Applying a fragment requires the content to match the stored counts, so
there must be a way to check this. Parsed fragments should always be
valid, but manually created or modified fragments may be invalid.
2020-01-05 20:36:01 -08:00
Billy Keyes d471b7e1a3 Add internal function to unwrap LineReaders
When applying, we need to copy all data after the last fragment line.
This function provides a way to get back the possibly-buffered io.Reader
that backs the LineReader.
2020-01-04 23:32:16 -08:00