gitdiff

package
v0.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 7, 2024 License: MIT Imports: 15 Imported by: 33

Documentation

Overview

Package gitdiff parses and applies patches generated by Git. It supports line-oriented text patches, binary patches, and can also parse standard unified diffs generated by other tools.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Apply added in v0.3.0

func Apply(dst io.Writer, src io.ReaderAt, f *File) error

Apply applies the changes in f to src, writing the result to dst. It can apply both text and binary changes.

If an error occurs while applying, Apply returns an *ApplyError that annotates the error with additional information. If the error is because of a conflict with the source, the wrapped error will be a *Conflict.

func ParsePatchDate added in v0.3.0

func ParsePatchDate(s string) (time.Time, error)

ParsePatchDate parses a patch date string. It returns the parsed time or an error if s has an unknown format. ParsePatchDate supports the iso, rfc, short, raw, unix, and default formats (with local variants) used by the --date flag in Git.

Types

type ApplyError added in v0.2.0

type ApplyError struct {
	// Line is the one-indexed line number in the source data
	Line int64
	// Fragment is the one-indexed fragment number in the file
	Fragment int
	// FragmentLine is the one-indexed line number in the fragment
	FragmentLine int
	// contains filtered or unexported fields
}

ApplyError wraps an error that occurs during patch application with additional location information, if it is available.

func (*ApplyError) Error added in v0.2.0

func (e *ApplyError) Error() string

func (*ApplyError) Unwrap added in v0.2.0

func (e *ApplyError) Unwrap() error

Unwrap returns the wrapped error.

type BinaryApplier added in v0.7.0

type BinaryApplier struct {
	// contains filtered or unexported fields
}

BinaryApplier applies binary changes described in a fragment to source data. The applier must be closed after use.

func NewBinaryApplier added in v0.7.0

func NewBinaryApplier(dst io.Writer, src io.ReaderAt) *BinaryApplier

NewBinaryApplier creates an BinaryApplier that reads data from src and writes modified data to dst.

func (*BinaryApplier) ApplyFragment added in v0.7.0

func (a *BinaryApplier) ApplyFragment(f *BinaryFragment) error

ApplyFragment applies the changes in the fragment f and writes the result to dst. ApplyFragment can be called at most once.

If an error occurs while applying, ApplyFragment returns an *ApplyError that annotates the error with additional information. If the error is because of a conflict between a fragment and the source, the wrapped error will be a *Conflict.

func (*BinaryApplier) Close added in v0.7.0

func (a *BinaryApplier) Close() (err error)

Close writes any data following the last applied fragment and prevents future calls to ApplyFragment.

type BinaryFragment

type BinaryFragment struct {
	Method BinaryPatchMethod
	Size   int64
	Data   []byte
}

BinaryFragment describes changes to a binary file.

type BinaryPatchMethod

type BinaryPatchMethod int

BinaryPatchMethod is the method used to create and apply the binary patch.

const (
	// BinaryPatchDelta indicates the data uses Git's packfile encoding
	BinaryPatchDelta BinaryPatchMethod = iota
	// BinaryPatchLiteral indicates the data is the exact file content
	BinaryPatchLiteral
)

type Conflict added in v0.2.0

type Conflict struct {
	// contains filtered or unexported fields
}

Conflict indicates an apply failed due to a conflict between the patch and the source content.

Users can test if an error was caused by a conflict by using errors.Is with an empty Conflict:

    if errors.Is(err, &Conflict{}) {
	       // handle conflict
    }

func (*Conflict) Error added in v0.2.0

func (c *Conflict) Error() string

func (*Conflict) Is added in v0.2.0

func (c *Conflict) Is(other error) bool

Is implements error matching for Conflict. Passing an empty instance of Conflict always returns true.

type File

type File struct {
	OldName string
	NewName string

	IsNew    bool
	IsDelete bool
	IsCopy   bool
	IsRename bool

	OldMode os.FileMode
	NewMode os.FileMode

	OldOIDPrefix string
	NewOIDPrefix string
	Score        int

	// TextFragments contains the fragments describing changes to a text file. It
	// may be empty if the file is empty or if only the mode changes.
	TextFragments []*TextFragment

	// IsBinary is true if the file is a binary file. If the patch includes
	// binary data, BinaryFragment will be non-nil and describe the changes to
	// the data. If the patch is reversible, ReverseBinaryFragment will also be
	// non-nil and describe the changes needed to restore the original file
	// after applying the changes in BinaryFragment.
	IsBinary              bool
	BinaryFragment        *BinaryFragment
	ReverseBinaryFragment *BinaryFragment
}

File describes changes to a single file. It can be either a text file or a binary file.

func Parse

func Parse(r io.Reader) ([]*File, string, error)

Parse parses a patch with changes to one or more files. Any content before the first file is returned as the second value. If an error occurs while parsing, it returns all files parsed before the error.

Parse expects to receive a single patch. If the input may contain multiple patches (for example, if it is an mbox file), callers should split it into individual patches and call Parse on each one.

type Line

type Line struct {
	Op   LineOp
	Line string
}

Line is a line in a text fragment.

func (Line) New added in v0.2.0

func (fl Line) New() bool

New returns true if the line appears in the new content of the fragment.

func (Line) NoEOL added in v0.2.0

func (fl Line) NoEOL() bool

NoEOL returns true if the line is missing a trailing newline character.

func (Line) Old added in v0.2.0

func (fl Line) Old() bool

Old returns true if the line appears in the old content of the fragment.

func (Line) String

func (fl Line) String() string

type LineOp

type LineOp int

LineOp describes the type of a text fragment line: context, added, or removed.

const (
	// OpContext indicates a context line
	OpContext LineOp = iota
	// OpDelete indicates a deleted line
	OpDelete
	// OpAdd indicates an added line
	OpAdd
)

func (LineOp) String

func (op LineOp) String() string

type LineReaderAt added in v0.2.0

type LineReaderAt interface {
	ReadLinesAt(lines [][]byte, offset int64) (n int, err error)
}

LineReaderAt is the interface that wraps the ReadLinesAt method.

ReadLinesAt reads len(lines) into lines starting at line offset. It returns the number of lines read (0 <= n <= len(lines)) and any error encountered. Line numbers are zero-indexed.

If n < len(lines), ReadLinesAt returns a non-nil error explaining why more lines were not returned.

Lines read by ReadLinesAt include the newline character. The last line does not have a final newline character if the input ends without one.

type PatchHeader added in v0.3.0

type PatchHeader struct {
	// The SHA of the commit the patch was generated from. Empty if the SHA is
	// not included in the header.
	SHA string

	// The author details of the patch. If these details are not included in
	// the header, Author is nil and AuthorDate is the zero time.
	Author     *PatchIdentity
	AuthorDate time.Time

	// The committer details of the patch. If these details are not included in
	// the header, Committer is nil and CommitterDate is the zero time.
	Committer     *PatchIdentity
	CommitterDate time.Time

	// The title and body of the commit message describing the changes in the
	// patch. Empty if no message is included in the header.
	Title string
	Body  string

	// If the preamble looks like an email, ParsePatchHeader will
	// remove prefixes such as `Re: ` and `[PATCH v3 5/17]` from the
	// Title and place them here.
	SubjectPrefix string

	// If the preamble looks like an email, and it contains a `---`
	// line, that line will be removed and everything after it will be
	// placed in BodyAppendix.
	BodyAppendix string
}

PatchHeader is a parsed version of the preamble content that appears before the first diff in a patch. It includes metadata about the patch, such as the author and a subject.

func ParsePatchHeader added in v0.3.0

func ParsePatchHeader(header string, options ...PatchHeaderOption) (*PatchHeader, error)

ParsePatchHeader parses the preamble string returned by Parse into a PatchHeader. Due to the variety of header formats, some fields of the parsed PatchHeader may be unset after parsing.

Supported formats are the short, medium, full, fuller, and email pretty formats used by `git diff`, `git log`, and `git show` and the UNIX mailbox format used by `git format-patch`.

When parsing mail-formatted headers, ParsePatchHeader tries to remove email-specific content from the title and body:

  • Based on the SubjectCleanMode, remove prefixes like reply markers and "[PATCH]" strings from the subject, saving any removed content in the SubjectPrefix field. Parsing always discards leading and trailing whitespace from the subject line. The default mode is SubjectCleanAll.

  • If the body contains a "---" line (3 hyphens), remove that line and any content after it from the body and save it in the BodyAppendix field.

ParsePatchHeader tries to process content it does not understand wthout returning errors, but will return errors if well-identified content like dates or identies uses unknown or invalid formats.

func (*PatchHeader) Message added in v0.3.0

func (h *PatchHeader) Message() string

Message returns the commit message for the header. The message consists of the title and the body separated by an empty line.

type PatchHeaderOption added in v0.7.0

type PatchHeaderOption func(*patchHeaderOptions)

A PatchHeaderOption modifies the behavior of ParsePatchHeader.

func WithSubjectCleanMode added in v0.7.0

func WithSubjectCleanMode(m SubjectCleanMode) PatchHeaderOption

WithSubjectCleanMode sets the SubjectCleanMode for header parsing. By default, uses SubjectCleanAll.

type PatchIdentity added in v0.3.0

type PatchIdentity struct {
	Name  string
	Email string
}

PatchIdentity identifies a person who authored or committed a patch.

func ParsePatchIdentity added in v0.3.0

func ParsePatchIdentity(s string) (PatchIdentity, error)

ParsePatchIdentity parses a patch identity string. A valid string contains an optional name followed by an email address in angle brackets. The angle brackets must always exist, but may enclose an empty address. At least one of the name or the email address must be non-empty. If the string only contains an email address, that value is also used as the name.

The name must not contain a left angle bracket, '<', and the email address must not contain a right angle bracket, '>'. Otherwise, there are no restrictions on the format of either field.

func (PatchIdentity) String added in v0.3.0

func (i PatchIdentity) String() string

type SubjectCleanMode added in v0.7.0

type SubjectCleanMode int

SubjectCleanMode controls how ParsePatchHeader cleans subject lines when parsing mail-formatted patches.

const (
	// SubjectCleanWhitespace removes leading and trailing whitespace.
	SubjectCleanWhitespace SubjectCleanMode = iota

	// SubjectCleanAll removes leading and trailing whitespace, leading "Re:",
	// "re:", and ":" strings, and leading strings enclosed by '[' and ']'.
	// This is the default behavior of git (see `git mailinfo`) and this
	// package.
	SubjectCleanAll

	// SubjectCleanPatchOnly is the same as SubjectCleanAll, but only removes
	// leading strings enclosed by '[' and ']' if they start with "PATCH".
	SubjectCleanPatchOnly
)

type TextApplier added in v0.7.0

type TextApplier struct {
	// contains filtered or unexported fields
}

TextApplier applies changes described in text fragments to source data. If changes are described in multiple fragments, those fragments must be applied in order. The applier must be closed after use.

By default, TextApplier operates in "strict" mode, where fragment content and positions must exactly match those of the source.

func NewTextApplier added in v0.7.0

func NewTextApplier(dst io.Writer, src io.ReaderAt) *TextApplier

NewTextApplier creates a TextApplier that reads data from src and writes modified data to dst. If src implements LineReaderAt, it is used directly.

func (*TextApplier) ApplyFragment added in v0.7.0

func (a *TextApplier) ApplyFragment(f *TextFragment) error

ApplyFragment applies the changes in the fragment f, writing unwritten data before the start of the fragment and any changes from the fragment. If multiple text fragments apply to the same content, ApplyFragment must be called in order of increasing start position. As a result, each fragment can be applied at most once.

If an error occurs while applying, ApplyFragment returns an *ApplyError that annotates the error with additional information. If the error is because of a conflict between the fragment and the source, the wrapped error will be a *Conflict.

func (*TextApplier) Close added in v0.7.0

func (a *TextApplier) Close() (err error)

Close writes any data following the last applied fragment and prevents future calls to ApplyFragment.

type TextFragment

type TextFragment struct {
	Comment string

	OldPosition int64
	OldLines    int64

	NewPosition int64
	NewLines    int64

	LinesAdded   int64
	LinesDeleted int64

	LeadingContext  int64
	TrailingContext int64

	Lines []Line
}

TextFragment describes changed lines starting at a specific line in a text file.

func (*TextFragment) Header

func (f *TextFragment) Header() string

Header returns the canonical header of this fragment.

func (*TextFragment) Validate added in v0.2.0

func (f *TextFragment) Validate() error

Validate checks that the fragment is self-consistent and appliable. Validate returns an error if and only if the fragment is invalid.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL