mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-11-05 06:21:24 +00:00
200 lines
4.8 KiB
Go
200 lines
4.8 KiB
Go
package gitdiff
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// File describes changes to a single file. It can be either a text file or a
|
|
// binary 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
|
|
}
|
|
|
|
// TextFragment describes changed lines starting at a specific line in a text file.
|
|
type TextFragment struct {
|
|
Comment string
|
|
|
|
OldPosition int64
|
|
OldLines int64
|
|
|
|
NewPosition int64
|
|
NewLines int64
|
|
|
|
LinesAdded int64
|
|
LinesDeleted int64
|
|
|
|
LeadingContext int64
|
|
TrailingContext int64
|
|
|
|
Lines []Line
|
|
}
|
|
|
|
// Header returns the canonical header of this fragment.
|
|
func (f *TextFragment) Header() string {
|
|
return fmt.Sprintf("@@ -%d,%d +%d,%d @@ %s", f.OldPosition, f.OldLines, f.NewPosition, f.NewLines, f.Comment)
|
|
}
|
|
|
|
// Validate checks that the fragment is self-consistent and appliable. Validate
|
|
// returns an error if and only if the fragment is invalid.
|
|
func (f *TextFragment) Validate() error {
|
|
if f == nil {
|
|
return errors.New("nil fragment")
|
|
}
|
|
|
|
var (
|
|
oldLines, newLines int64
|
|
leadingContext, trailingContext int64
|
|
contextLines, addedLines, deletedLines int64
|
|
)
|
|
|
|
// count the types of lines in the fragment content
|
|
for i, line := range f.Lines {
|
|
switch line.Op {
|
|
case OpContext:
|
|
oldLines++
|
|
newLines++
|
|
contextLines++
|
|
if addedLines == 0 && deletedLines == 0 {
|
|
leadingContext++
|
|
} else {
|
|
trailingContext++
|
|
}
|
|
case OpAdd:
|
|
newLines++
|
|
addedLines++
|
|
trailingContext = 0
|
|
case OpDelete:
|
|
oldLines++
|
|
deletedLines++
|
|
trailingContext = 0
|
|
default:
|
|
return fmt.Errorf("unknown operator %q on line %d", line.Op, i+1)
|
|
}
|
|
}
|
|
|
|
// check the actual counts against the reported counts
|
|
if oldLines != f.OldLines {
|
|
return lineCountErr("old", oldLines, f.OldLines)
|
|
}
|
|
if newLines != f.NewLines {
|
|
return lineCountErr("new", newLines, f.NewLines)
|
|
}
|
|
if leadingContext != f.LeadingContext {
|
|
return lineCountErr("leading context", leadingContext, f.LeadingContext)
|
|
}
|
|
if trailingContext != f.TrailingContext {
|
|
return lineCountErr("trailing context", trailingContext, f.TrailingContext)
|
|
}
|
|
if addedLines != f.LinesAdded {
|
|
return lineCountErr("added", addedLines, f.LinesAdded)
|
|
}
|
|
if deletedLines != f.LinesDeleted {
|
|
return lineCountErr("deleted", deletedLines, f.LinesDeleted)
|
|
}
|
|
|
|
// if a file is being created, it can only contain additions
|
|
if f.OldPosition == 0 && f.OldLines != 0 {
|
|
return errors.New("file creation fragment contains context or deletion lines")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func lineCountErr(kind string, actual, reported int64) error {
|
|
return fmt.Errorf("fragment contains %d %s lines but reports %d", actual, kind, reported)
|
|
}
|
|
|
|
// Line is a line in a text fragment.
|
|
type Line struct {
|
|
Op LineOp
|
|
Line string
|
|
}
|
|
|
|
func (fl Line) String() string {
|
|
return fl.Op.String() + fl.Line
|
|
}
|
|
|
|
// Old returns true if the line appears in the old content of the fragment.
|
|
func (fl Line) Old() bool {
|
|
return fl.Op == OpContext || fl.Op == OpDelete
|
|
}
|
|
|
|
// New returns true if the line appears in the new content of the fragment.
|
|
func (fl Line) New() bool {
|
|
return fl.Op == OpContext || fl.Op == OpAdd
|
|
}
|
|
|
|
// NoEOL returns true if the line is missing a trailing newline character.
|
|
func (fl Line) NoEOL() bool {
|
|
return len(fl.Line) == 0 || fl.Line[len(fl.Line)-1] != '\n'
|
|
}
|
|
|
|
// LineOp describes the type of a text fragment line: context, added, or removed.
|
|
type LineOp int
|
|
|
|
const (
|
|
// OpContext indicates a context line
|
|
OpContext LineOp = iota
|
|
// OpDelete indicates a deleted line
|
|
OpDelete
|
|
// OpAdd indicates an added line
|
|
OpAdd
|
|
)
|
|
|
|
func (op LineOp) String() string {
|
|
switch op {
|
|
case OpContext:
|
|
return " "
|
|
case OpDelete:
|
|
return "-"
|
|
case OpAdd:
|
|
return "+"
|
|
}
|
|
return "?"
|
|
}
|
|
|
|
// BinaryFragment describes changes to a binary file.
|
|
type BinaryFragment struct {
|
|
Method BinaryPatchMethod
|
|
Size int64
|
|
Data []byte
|
|
}
|
|
|
|
// BinaryPatchMethod is the method used to create and apply the binary patch.
|
|
type BinaryPatchMethod int
|
|
|
|
const (
|
|
// BinaryPatchDelta indicates the data uses Git's packfile encoding
|
|
BinaryPatchDelta BinaryPatchMethod = iota
|
|
// BinaryPatchLiteral indicates the data is the exact file content
|
|
BinaryPatchLiteral
|
|
)
|