github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libgit/diff_file.go (about)

     1  // Copyright 2019 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libgit
     6  
     7  import (
     8  	"context"
     9  	"io"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	billy "gopkg.in/src-d/go-billy.v4"
    15  	"gopkg.in/src-d/go-git.v4/plumbing/object"
    16  )
    17  
    18  const (
    19  	diffFileDefaultMaxBufSize = 4 * 1024 * 1024 // 4 MB
    20  )
    21  
    22  type diffFile struct {
    23  	name       string
    24  	r          *strings.Reader
    25  	maxBufSize int64
    26  	mtime      time.Time
    27  }
    28  
    29  var _ billy.File = (*diffFile)(nil)
    30  
    31  func newDiffFile(
    32  	ctx context.Context, from, to *object.Commit, header, name string) (
    33  	*diffFile, error) {
    34  	s := header
    35  	patch, err := from.PatchContext(ctx, to)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	s += patch.String()
    40  
    41  	r := strings.NewReader(s)
    42  	return &diffFile{
    43  		name:       name,
    44  		r:          r,
    45  		maxBufSize: diffFileDefaultMaxBufSize,
    46  		mtime:      to.Author.When,
    47  	}, nil
    48  }
    49  
    50  func (df *diffFile) Len() int {
    51  	return df.r.Len()
    52  }
    53  
    54  func (df *diffFile) Name() string {
    55  	return df.name
    56  }
    57  
    58  func (df *diffFile) Write(_ []byte) (n int, err error) {
    59  	return 0, errors.New("diff files can't be written")
    60  }
    61  
    62  func (df *diffFile) Read(p []byte) (n int, err error) {
    63  	return df.r.Read(p)
    64  }
    65  
    66  func (df *diffFile) ReadAt(p []byte, off int64) (n int, err error) {
    67  	n, err = df.r.ReadAt(p, off)
    68  	if err == io.EOF {
    69  		if n == 0 {
    70  			// The billy interface only likes EOFs when no data was read.
    71  			return 0, err
    72  		}
    73  	} else if err != nil {
    74  		return 0, err
    75  	}
    76  	return n, nil
    77  }
    78  
    79  func (df *diffFile) Seek(offset int64, whence int) (int64, error) {
    80  	return df.r.Seek(offset, whence)
    81  }
    82  
    83  func (df *diffFile) Close() error {
    84  	return nil
    85  }
    86  
    87  func (df *diffFile) Lock() error {
    88  	return errors.New("diff files can't be locked")
    89  }
    90  
    91  func (df *diffFile) Unlock() error {
    92  	return errors.New("diff files can't be unlocked")
    93  }
    94  
    95  func (df *diffFile) Truncate(size int64) error {
    96  	return errors.New("diff files can't be truncated")
    97  }
    98  
    99  func (df *diffFile) GetInfo() *diffFileInfo {
   100  	return &diffFileInfo{
   101  		name:  df.Name(),
   102  		size:  int64(df.Len()),
   103  		mtime: df.mtime,
   104  	}
   105  }
   106  
   107  func newCommitFile(
   108  	ctx context.Context, commit *object.Commit) (*diffFile, error) {
   109  	header := commit.String()
   110  	name := AutogitCommitPrefix + commit.Hash.String()
   111  	// We can't get the patch for the initial commit, go-git doesn't
   112  	// seem to support it yet.
   113  	parent := commit
   114  	if commit.NumParents() > 0 {
   115  		var err error
   116  		parent, err = commit.Parent(0)
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  	}
   121  	return newDiffFile(ctx, parent, commit, header, name)
   122  }