feat(dir2config): defaults
This commit is contained in:
335
vendor/gopkg.in/src-d/go-git.v4/plumbing/object/patch.go
generated
vendored
Normal file
335
vendor/gopkg.in/src-d/go-git.v4/plumbing/object/patch.go
generated
vendored
Normal file
@ -0,0 +1,335 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
|
||||
fdiff "gopkg.in/src-d/go-git.v4/plumbing/format/diff"
|
||||
"gopkg.in/src-d/go-git.v4/utils/diff"
|
||||
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCanceled = errors.New("operation canceled")
|
||||
)
|
||||
|
||||
func getPatch(message string, changes ...*Change) (*Patch, error) {
|
||||
ctx := context.Background()
|
||||
return getPatchContext(ctx, message, changes...)
|
||||
}
|
||||
|
||||
func getPatchContext(ctx context.Context, message string, changes ...*Change) (*Patch, error) {
|
||||
var filePatches []fdiff.FilePatch
|
||||
for _, c := range changes {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ErrCanceled
|
||||
default:
|
||||
}
|
||||
|
||||
fp, err := filePatchWithContext(ctx, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filePatches = append(filePatches, fp)
|
||||
}
|
||||
|
||||
return &Patch{message, filePatches}, nil
|
||||
}
|
||||
|
||||
func filePatchWithContext(ctx context.Context, c *Change) (fdiff.FilePatch, error) {
|
||||
from, to, err := c.Files()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fromContent, fIsBinary, err := fileContent(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toContent, tIsBinary, err := fileContent(to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fIsBinary || tIsBinary {
|
||||
return &textFilePatch{from: c.From, to: c.To}, nil
|
||||
}
|
||||
|
||||
diffs := diff.Do(fromContent, toContent)
|
||||
|
||||
var chunks []fdiff.Chunk
|
||||
for _, d := range diffs {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ErrCanceled
|
||||
default:
|
||||
}
|
||||
|
||||
var op fdiff.Operation
|
||||
switch d.Type {
|
||||
case dmp.DiffEqual:
|
||||
op = fdiff.Equal
|
||||
case dmp.DiffDelete:
|
||||
op = fdiff.Delete
|
||||
case dmp.DiffInsert:
|
||||
op = fdiff.Add
|
||||
}
|
||||
|
||||
chunks = append(chunks, &textChunk{d.Text, op})
|
||||
}
|
||||
|
||||
return &textFilePatch{
|
||||
chunks: chunks,
|
||||
from: c.From,
|
||||
to: c.To,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func filePatch(c *Change) (fdiff.FilePatch, error) {
|
||||
return filePatchWithContext(context.Background(), c)
|
||||
}
|
||||
|
||||
func fileContent(f *File) (content string, isBinary bool, err error) {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
|
||||
isBinary, err = f.IsBinary()
|
||||
if err != nil || isBinary {
|
||||
return
|
||||
}
|
||||
|
||||
content, err = f.Contents()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// textPatch is an implementation of fdiff.Patch interface
|
||||
type Patch struct {
|
||||
message string
|
||||
filePatches []fdiff.FilePatch
|
||||
}
|
||||
|
||||
func (t *Patch) FilePatches() []fdiff.FilePatch {
|
||||
return t.filePatches
|
||||
}
|
||||
|
||||
func (t *Patch) Message() string {
|
||||
return t.message
|
||||
}
|
||||
|
||||
func (p *Patch) Encode(w io.Writer) error {
|
||||
ue := fdiff.NewUnifiedEncoder(w, fdiff.DefaultContextLines)
|
||||
|
||||
return ue.Encode(p)
|
||||
}
|
||||
|
||||
func (p *Patch) Stats() FileStats {
|
||||
return getFileStatsFromFilePatches(p.FilePatches())
|
||||
}
|
||||
|
||||
func (p *Patch) String() string {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err := p.Encode(buf)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("malformed patch: %s", err.Error())
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// changeEntryWrapper is an implementation of fdiff.File interface
|
||||
type changeEntryWrapper struct {
|
||||
ce ChangeEntry
|
||||
}
|
||||
|
||||
func (f *changeEntryWrapper) Hash() plumbing.Hash {
|
||||
if !f.ce.TreeEntry.Mode.IsFile() {
|
||||
return plumbing.ZeroHash
|
||||
}
|
||||
|
||||
return f.ce.TreeEntry.Hash
|
||||
}
|
||||
|
||||
func (f *changeEntryWrapper) Mode() filemode.FileMode {
|
||||
return f.ce.TreeEntry.Mode
|
||||
}
|
||||
func (f *changeEntryWrapper) Path() string {
|
||||
if !f.ce.TreeEntry.Mode.IsFile() {
|
||||
return ""
|
||||
}
|
||||
|
||||
return f.ce.Name
|
||||
}
|
||||
|
||||
func (f *changeEntryWrapper) Empty() bool {
|
||||
return !f.ce.TreeEntry.Mode.IsFile()
|
||||
}
|
||||
|
||||
// textFilePatch is an implementation of fdiff.FilePatch interface
|
||||
type textFilePatch struct {
|
||||
chunks []fdiff.Chunk
|
||||
from, to ChangeEntry
|
||||
}
|
||||
|
||||
func (tf *textFilePatch) Files() (from fdiff.File, to fdiff.File) {
|
||||
f := &changeEntryWrapper{tf.from}
|
||||
t := &changeEntryWrapper{tf.to}
|
||||
|
||||
if !f.Empty() {
|
||||
from = f
|
||||
}
|
||||
|
||||
if !t.Empty() {
|
||||
to = t
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (t *textFilePatch) IsBinary() bool {
|
||||
return len(t.chunks) == 0
|
||||
}
|
||||
|
||||
func (t *textFilePatch) Chunks() []fdiff.Chunk {
|
||||
return t.chunks
|
||||
}
|
||||
|
||||
// textChunk is an implementation of fdiff.Chunk interface
|
||||
type textChunk struct {
|
||||
content string
|
||||
op fdiff.Operation
|
||||
}
|
||||
|
||||
func (t *textChunk) Content() string {
|
||||
return t.content
|
||||
}
|
||||
|
||||
func (t *textChunk) Type() fdiff.Operation {
|
||||
return t.op
|
||||
}
|
||||
|
||||
// FileStat stores the status of changes in content of a file.
|
||||
type FileStat struct {
|
||||
Name string
|
||||
Addition int
|
||||
Deletion int
|
||||
}
|
||||
|
||||
func (fs FileStat) String() string {
|
||||
return printStat([]FileStat{fs})
|
||||
}
|
||||
|
||||
// FileStats is a collection of FileStat.
|
||||
type FileStats []FileStat
|
||||
|
||||
func (fileStats FileStats) String() string {
|
||||
return printStat(fileStats)
|
||||
}
|
||||
|
||||
func printStat(fileStats []FileStat) string {
|
||||
padLength := float64(len(" "))
|
||||
newlineLength := float64(len("\n"))
|
||||
separatorLength := float64(len("|"))
|
||||
// Soft line length limit. The text length calculation below excludes
|
||||
// length of the change number. Adding that would take it closer to 80,
|
||||
// but probably not more than 80, until it's a huge number.
|
||||
lineLength := 72.0
|
||||
|
||||
// Get the longest filename and longest total change.
|
||||
var longestLength float64
|
||||
var longestTotalChange float64
|
||||
for _, fs := range fileStats {
|
||||
if int(longestLength) < len(fs.Name) {
|
||||
longestLength = float64(len(fs.Name))
|
||||
}
|
||||
totalChange := fs.Addition + fs.Deletion
|
||||
if int(longestTotalChange) < totalChange {
|
||||
longestTotalChange = float64(totalChange)
|
||||
}
|
||||
}
|
||||
|
||||
// Parts of the output:
|
||||
// <pad><filename><pad>|<pad><changeNumber><pad><+++/---><newline>
|
||||
// example: " main.go | 10 +++++++--- "
|
||||
|
||||
// <pad><filename><pad>
|
||||
leftTextLength := padLength + longestLength + padLength
|
||||
|
||||
// <pad><number><pad><+++++/-----><newline>
|
||||
// Excluding number length here.
|
||||
rightTextLength := padLength + padLength + newlineLength
|
||||
|
||||
totalTextArea := leftTextLength + separatorLength + rightTextLength
|
||||
heightOfHistogram := lineLength - totalTextArea
|
||||
|
||||
// Scale the histogram.
|
||||
var scaleFactor float64
|
||||
if longestTotalChange > heightOfHistogram {
|
||||
// Scale down to heightOfHistogram.
|
||||
scaleFactor = float64(longestTotalChange / heightOfHistogram)
|
||||
} else {
|
||||
scaleFactor = 1.0
|
||||
}
|
||||
|
||||
finalOutput := ""
|
||||
for _, fs := range fileStats {
|
||||
addn := float64(fs.Addition)
|
||||
deln := float64(fs.Deletion)
|
||||
adds := strings.Repeat("+", int(math.Floor(addn/scaleFactor)))
|
||||
dels := strings.Repeat("-", int(math.Floor(deln/scaleFactor)))
|
||||
finalOutput += fmt.Sprintf(" %s | %d %s%s\n", fs.Name, (fs.Addition + fs.Deletion), adds, dels)
|
||||
}
|
||||
|
||||
return finalOutput
|
||||
}
|
||||
|
||||
func getFileStatsFromFilePatches(filePatches []fdiff.FilePatch) FileStats {
|
||||
var fileStats FileStats
|
||||
|
||||
for _, fp := range filePatches {
|
||||
// ignore empty patches (binary files, submodule refs updates)
|
||||
if len(fp.Chunks()) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cs := FileStat{}
|
||||
from, to := fp.Files()
|
||||
if from == nil {
|
||||
// New File is created.
|
||||
cs.Name = to.Path()
|
||||
} else if to == nil {
|
||||
// File is deleted.
|
||||
cs.Name = from.Path()
|
||||
} else if from.Path() != to.Path() {
|
||||
// File is renamed. Not supported.
|
||||
// cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path())
|
||||
} else {
|
||||
cs.Name = from.Path()
|
||||
}
|
||||
|
||||
for _, chunk := range fp.Chunks() {
|
||||
switch chunk.Type() {
|
||||
case fdiff.Add:
|
||||
cs.Addition += strings.Count(chunk.Content(), "\n")
|
||||
case fdiff.Delete:
|
||||
cs.Deletion += strings.Count(chunk.Content(), "\n")
|
||||
}
|
||||
}
|
||||
|
||||
fileStats = append(fileStats, cs)
|
||||
}
|
||||
|
||||
return fileStats
|
||||
}
|
Reference in New Issue
Block a user