1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-28 16:02:01 +03:00

Bump go-git

This commit is contained in:
Stefan Haller
2025-04-09 10:38:46 +02:00
parent da0105c16b
commit 4cf49ff449
527 changed files with 70489 additions and 10167 deletions

View File

@ -7,19 +7,22 @@ import (
"errors"
"fmt"
"io"
stdioutil "io/ioutil"
"os"
"path"
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
"time"
"github.com/go-git/go-billy/v5/osfs"
"github.com/jesseduffield/go-git/v5/plumbing"
"github.com/jesseduffield/go-git/v5/plumbing/hash"
"github.com/jesseduffield/go-git/v5/storage"
"github.com/jesseduffield/go-git/v5/utils/ioutil"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/helper/chroot"
)
const (
@ -38,6 +41,7 @@ const (
remotesPath = "remotes"
logsPath = "logs"
worktreesPath = "worktrees"
alternatesPath = "alternates"
tmpPackedRefsPrefix = "._packed-refs"
@ -68,6 +72,9 @@ var (
// ErrIsDir is returned when a reference file is attempting to be read,
// but the path specified is a directory.
ErrIsDir = errors.New("reference path is a directory")
// ErrEmptyRefFile is returned when a reference file is attempted to be read,
// but the file is empty
ErrEmptyRefFile = errors.New("ref file is empty")
)
// Options holds configuration for the storage.
@ -78,6 +85,10 @@ type Options struct {
// KeepDescriptors makes the file descriptors to be reused but they will
// need to be manually closed calling Close().
KeepDescriptors bool
// AlternatesFS provides the billy filesystem to be used for Git Alternates.
// If none is provided, it falls back to using the underlying instance used for
// DotGit.
AlternatesFS billy.Filesystem
}
// The DotGit type represents a local git repository on disk. This
@ -241,7 +252,7 @@ func (d *DotGit) objectPacks() ([]plumbing.Hash, error) {
continue
}
h := plumbing.NewHash(n[5 : len(n)-5]) //pack-(hash).pack
h := plumbing.NewHash(n[5 : len(n)-5]) // pack-(hash).pack
if h.IsZero() {
// Ignore files with badly-formatted names.
continue
@ -552,8 +563,8 @@ func (d *DotGit) hasPack(h plumbing.Hash) error {
}
func (d *DotGit) objectPath(h plumbing.Hash) string {
hash := h.String()
return d.fs.Join(objectsPath, hash[0:2], hash[2:40])
hex := h.String()
return d.fs.Join(objectsPath, hex[0:2], hex[2:hash.HexSize])
}
// incomingObjectPath is intended to add support for a git pre-receive hook
@ -563,15 +574,16 @@ func (d *DotGit) objectPath(h plumbing.Hash) string {
//
// More on git hooks found here : https://git-scm.com/docs/githooks
// More on 'quarantine'/incoming directory here:
// https://git-scm.com/docs/git-receive-pack
//
// https://git-scm.com/docs/git-receive-pack
func (d *DotGit) incomingObjectPath(h plumbing.Hash) string {
hString := h.String()
if d.incomingDirName == "" {
return d.fs.Join(objectsPath, hString[0:2], hString[2:40])
return d.fs.Join(objectsPath, hString[0:2], hString[2:hash.HexSize])
}
return d.fs.Join(objectsPath, d.incomingDirName, hString[0:2], hString[2:40])
return d.fs.Join(objectsPath, d.incomingDirName, hString[0:2], hString[2:hash.HexSize])
}
// hasIncomingObjects searches for an incoming directory and keeps its name
@ -581,7 +593,9 @@ func (d *DotGit) hasIncomingObjects() bool {
directoryContents, err := d.fs.ReadDir(objectsPath)
if err == nil {
for _, file := range directoryContents {
if strings.HasPrefix(file.Name(), "incoming-") && file.IsDir() {
if file.IsDir() && (strings.HasPrefix(file.Name(), "tmp_objdir-incoming-") ||
// Before Git 2.35 incoming commits directory had another prefix
strings.HasPrefix(file.Name(), "incoming-")) {
d.incomingDirName = file.Name()
}
}
@ -645,23 +659,38 @@ func (d *DotGit) ObjectDelete(h plumbing.Hash) error {
}
func (d *DotGit) readReferenceFrom(rd io.Reader, name string) (ref *plumbing.Reference, err error) {
b, err := stdioutil.ReadAll(rd)
b, err := io.ReadAll(rd)
if err != nil {
return nil, err
}
if len(b) == 0 {
return nil, ErrEmptyRefFile
}
line := strings.TrimSpace(string(b))
return plumbing.NewReferenceFromStrings(name, line), nil
}
// checkReferenceAndTruncate reads the reference from the given file, or the `pack-refs` file if
// the file was empty. Then it checks that the old reference matches the stored reference and
// truncates the file.
func (d *DotGit) checkReferenceAndTruncate(f billy.File, old *plumbing.Reference) error {
if old == nil {
return nil
}
ref, err := d.readReferenceFrom(f, old.Name().String())
if errors.Is(err, ErrEmptyRefFile) {
// This may happen if the reference is being read from a newly created file.
// In that case, try getting the reference from the packed refs file.
ref, err = d.packedRef(old.Name())
}
if err != nil {
return err
}
if ref.Hash() != old.Hash() {
return storage.ErrReferenceHasChanged
}
@ -690,7 +719,11 @@ func (d *DotGit) SetRef(r, old *plumbing.Reference) error {
// Symbolic references are resolved and included in the output.
func (d *DotGit) Refs() ([]*plumbing.Reference, error) {
var refs []*plumbing.Reference
var seen = make(map[plumbing.ReferenceName]bool)
seen := make(map[plumbing.ReferenceName]bool)
if err := d.addRefFromHEAD(&refs); err != nil {
return nil, err
}
if err := d.addRefsFromRefDir(&refs, seen); err != nil {
return nil, err
}
@ -699,10 +732,6 @@ func (d *DotGit) Refs() ([]*plumbing.Reference, error) {
return nil, err
}
if err := d.addRefFromHEAD(&refs); err != nil {
return nil, err
}
return refs, nil
}
@ -716,48 +745,56 @@ func (d *DotGit) Ref(name plumbing.ReferenceName) (*plumbing.Reference, error) {
return d.packedRef(name)
}
func (d *DotGit) findPackedRefsInFile(f billy.File) ([]*plumbing.Reference, error) {
func (d *DotGit) findPackedRefsInFile(f billy.File, recv refsRecv) error {
s := bufio.NewScanner(f)
var refs []*plumbing.Reference
for s.Scan() {
ref, err := d.processLine(s.Text())
if err != nil {
return nil, err
return err
}
if ref != nil {
refs = append(refs, ref)
if !recv(ref) {
// skip parse
return nil
}
}
return refs, s.Err()
if err := s.Err(); err != nil {
return err
}
return nil
}
func (d *DotGit) findPackedRefs() (r []*plumbing.Reference, err error) {
// refsRecv: returning true means that the reference continues to be resolved, otherwise it is stopped, which will speed up the lookup of a single reference.
type refsRecv func(*plumbing.Reference) bool
func (d *DotGit) findPackedRefs(recv refsRecv) error {
f, err := d.fs.Open(packedRefsPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
return nil
}
return nil, err
return err
}
defer ioutil.CheckClose(f, &err)
return d.findPackedRefsInFile(f)
return d.findPackedRefsInFile(f, recv)
}
func (d *DotGit) packedRef(name plumbing.ReferenceName) (*plumbing.Reference, error) {
refs, err := d.findPackedRefs()
if err != nil {
var ref *plumbing.Reference
if err := d.findPackedRefs(func(r *plumbing.Reference) bool {
if r != nil && r.Name() == name {
ref = r
// ref found
return false
}
return true
}); err != nil {
return nil, err
}
for _, ref := range refs {
if ref.Name() == name {
return ref, nil
}
if ref != nil {
return ref, nil
}
return nil, plumbing.ErrReferenceNotFound
}
@ -777,38 +814,27 @@ func (d *DotGit) RemoveRef(name plumbing.ReferenceName) error {
return d.rewritePackedRefsWithoutRef(name)
}
func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference, seen map[plumbing.ReferenceName]bool) (err error) {
packedRefs, err := d.findPackedRefs()
if err != nil {
return err
}
for _, ref := range packedRefs {
if !seen[ref.Name()] {
*refs = append(*refs, ref)
seen[ref.Name()] = true
func refsRecvFunc(refs *[]*plumbing.Reference, seen map[plumbing.ReferenceName]bool) refsRecv {
return func(r *plumbing.Reference) bool {
if r != nil && !seen[r.Name()] {
*refs = append(*refs, r)
seen[r.Name()] = true
}
return true
}
return nil
}
func (d *DotGit) addRefsFromPackedRefs(refs *[]*plumbing.Reference, seen map[plumbing.ReferenceName]bool) (err error) {
return d.findPackedRefs(refsRecvFunc(refs, seen))
}
func (d *DotGit) addRefsFromPackedRefsFile(refs *[]*plumbing.Reference, f billy.File, seen map[plumbing.ReferenceName]bool) (err error) {
packedRefs, err := d.findPackedRefsInFile(f)
if err != nil {
return err
}
for _, ref := range packedRefs {
if !seen[ref.Name()] {
*refs = append(*refs, ref)
seen[ref.Name()] = true
}
}
return nil
return d.findPackedRefsInFile(f, refsRecvFunc(refs, seen))
}
func (d *DotGit) openAndLockPackedRefs(doCreate bool) (
pr billy.File, err error) {
pr billy.File, err error,
) {
var f billy.File
defer func() {
if err != nil && f != nil {
@ -943,6 +969,7 @@ func (d *DotGit) walkReferencesTree(refs *[]*plumbing.Reference, relPath []strin
files, err := d.fs.ReadDir(d.fs.Join(relPath...))
if err != nil {
if os.IsNotExist(err) {
// a race happened, and our directory is gone now
return nil
}
@ -960,6 +987,10 @@ func (d *DotGit) walkReferencesTree(refs *[]*plumbing.Reference, relPath []strin
}
ref, err := d.readReferenceFile(".", strings.Join(newRelPath, "/"))
if os.IsNotExist(err) {
// a race happened, and our file is gone now
continue
}
if err != nil {
return err
}
@ -1008,7 +1039,7 @@ func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference,
func (d *DotGit) CountLooseRefs() (int, error) {
var refs []*plumbing.Reference
var seen = make(map[plumbing.ReferenceName]bool)
seen := make(map[plumbing.ReferenceName]bool)
if err := d.addRefsFromRefDir(&refs, seen); err != nil {
return 0, err
}
@ -1101,38 +1132,93 @@ func (d *DotGit) Module(name string) (billy.Filesystem, error) {
return d.fs.Chroot(d.fs.Join(modulePath, name))
}
func (d *DotGit) AddAlternate(remote string) error {
altpath := d.fs.Join(objectsPath, infoPath, alternatesPath)
f, err := d.fs.OpenFile(altpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
if err != nil {
return fmt.Errorf("cannot open file: %w", err)
}
defer f.Close()
// locking in windows throws an error, based on comments
// https://github.com/go-git/go-git/pull/860#issuecomment-1751823044
// do not lock on windows platform.
if runtime.GOOS != "windows" {
if err = f.Lock(); err != nil {
return fmt.Errorf("cannot lock file: %w", err)
}
defer f.Unlock()
}
line := path.Join(remote, objectsPath) + "\n"
_, err = io.WriteString(f, line)
if err != nil {
return fmt.Errorf("error writing 'alternates' file: %w", err)
}
return nil
}
// Alternates returns DotGit(s) based off paths in objects/info/alternates if
// available. This can be used to checks if it's a shared repository.
func (d *DotGit) Alternates() ([]*DotGit, error) {
altpath := d.fs.Join("objects", "info", "alternates")
altpath := d.fs.Join(objectsPath, infoPath, alternatesPath)
f, err := d.fs.Open(altpath)
if err != nil {
return nil, err
}
defer f.Close()
fs := d.options.AlternatesFS
if fs == nil {
fs = d.fs
}
var alternates []*DotGit
seen := make(map[string]struct{})
// Read alternate paths line-by-line and create DotGit objects.
scanner := bufio.NewScanner(f)
for scanner.Scan() {
path := scanner.Text()
if !filepath.IsAbs(path) {
// For relative paths, we can perform an internal conversion to
// slash so that they work cross-platform.
slashPath := filepath.ToSlash(path)
// If the path is not absolute, it must be relative to object
// database (.git/objects/info).
// https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html
// Hence, derive a path relative to DotGit's root.
// "../../../reponame/.git/" -> "../../reponame/.git"
// Remove the first ../
relpath := filepath.Join(strings.Split(slashPath, "/")[1:]...)
normalPath := filepath.FromSlash(relpath)
path = filepath.Join(d.fs.Root(), normalPath)
// Avoid creating multiple dotgits for the same alternative path.
if _, ok := seen[path]; ok {
continue
}
fs := osfs.New(filepath.Dir(path))
alternates = append(alternates, New(fs))
seen[path] = struct{}{}
if filepath.IsAbs(path) {
// Handling absolute paths should be straight-forward. However, the default osfs (Chroot)
// tries to concatenate an abs path with the root path in some operations (e.g. Stat),
// which leads to unexpected errors. Therefore, make the path relative to the current FS instead.
if reflect.TypeOf(fs) == reflect.TypeOf(&chroot.ChrootHelper{}) {
path, err = filepath.Rel(fs.Root(), path)
if err != nil {
return nil, fmt.Errorf("cannot make path %q relative: %w", path, err)
}
}
} else {
// By Git conventions, relative paths should be based on the object database (.git/objects/info)
// location as per: https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html
// However, due to the nature of go-git and its filesystem handling via Billy, paths cannot
// cross its "chroot boundaries". Therefore, ignore any "../" and treat the path from the
// fs root. If this is not correct based on the dotgit fs, set a different one via AlternatesFS.
abs := filepath.Join(string(filepath.Separator), filepath.ToSlash(path))
path = filepath.FromSlash(abs)
}
// Aligns with upstream behavior: exit if target path is not a valid directory.
if fi, err := fs.Stat(path); err != nil || !fi.IsDir() {
return nil, fmt.Errorf("invalid object directory %q: %w", path, err)
}
afs, err := fs.Chroot(filepath.Dir(path))
if err != nil {
return nil, fmt.Errorf("cannot chroot %q: %w", path, err)
}
alternates = append(alternates, New(afs))
}
if err = scanner.Err(); err != nil {

View File

@ -0,0 +1,79 @@
package dotgit
import (
"fmt"
"io"
"os"
"github.com/jesseduffield/go-git/v5/plumbing"
"github.com/jesseduffield/go-git/v5/plumbing/format/objfile"
"github.com/jesseduffield/go-git/v5/utils/ioutil"
)
var _ (plumbing.EncodedObject) = &EncodedObject{}
type EncodedObject struct {
dir *DotGit
h plumbing.Hash
t plumbing.ObjectType
sz int64
}
func (e *EncodedObject) Hash() plumbing.Hash {
return e.h
}
func (e *EncodedObject) Reader() (io.ReadCloser, error) {
f, err := e.dir.Object(e.h)
if err != nil {
if os.IsNotExist(err) {
return nil, plumbing.ErrObjectNotFound
}
return nil, err
}
r, err := objfile.NewReader(f)
if err != nil {
return nil, err
}
t, size, err := r.Header()
if err != nil {
_ = r.Close()
return nil, err
}
if t != e.t {
_ = r.Close()
return nil, objfile.ErrHeader
}
if size != e.sz {
_ = r.Close()
return nil, objfile.ErrHeader
}
return ioutil.NewReadCloserWithCloser(r, f.Close), nil
}
func (e *EncodedObject) SetType(plumbing.ObjectType) {}
func (e *EncodedObject) Type() plumbing.ObjectType {
return e.t
}
func (e *EncodedObject) Size() int64 {
return e.sz
}
func (e *EncodedObject) SetSize(int64) {}
func (e *EncodedObject) Writer() (io.WriteCloser, error) {
return nil, fmt.Errorf("not supported")
}
func NewEncodedObject(dir *DotGit, h plumbing.Hash, t plumbing.ObjectType, size int64) *EncodedObject {
return &EncodedObject{
dir: dir,
h: h,
t: t,
sz: size,
}
}

View File

@ -9,6 +9,7 @@ import (
"github.com/jesseduffield/go-git/v5/plumbing/format/idxfile"
"github.com/jesseduffield/go-git/v5/plumbing/format/objfile"
"github.com/jesseduffield/go-git/v5/plumbing/format/packfile"
"github.com/jesseduffield/go-git/v5/plumbing/hash"
"github.com/go-git/go-billy/v5"
)
@ -277,8 +278,8 @@ func (w *ObjectWriter) Close() error {
}
func (w *ObjectWriter) save() error {
hash := w.Hash().String()
file := w.fs.Join(objectsPath, hash[0:2], hash[2:40])
hex := w.Hash().String()
file := w.fs.Join(objectsPath, hex[0:2], hex[2:hash.HexSize])
return w.fs.Rename(w.f.Name(), file)
}

View File

@ -48,7 +48,7 @@ func (s *IndexStorage) Index() (i *index.Index, err error) {
defer ioutil.CheckClose(f, &err)
d := index.NewDecoder(bufio.NewReader(f))
d := index.NewDecoder(f)
err = d.Decode(idx)
return idx, err
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"io"
"os"
"sync"
"time"
"github.com/jesseduffield/go-git/v5/plumbing"
@ -145,6 +146,19 @@ func (s *ObjectStorage) SetEncodedObject(o plumbing.EncodedObject) (h plumbing.H
return o.Hash(), err
}
// LazyWriter returns a lazy ObjectWriter that is bound to a DotGit file.
// It first write the header passing on the object type and size, so
// that the object contents can be written later, without the need to
// create a MemoryObject and buffering its entire contents into memory.
func (s *ObjectStorage) LazyWriter() (w io.WriteCloser, wh func(typ plumbing.ObjectType, sz int64) error, err error) {
ow, err := s.dir.NewObject()
if err != nil {
return nil, nil, err
}
return ow, ow.WriteHeader, nil
}
// HasEncodedObject returns nil if the object exists, without actually
// reading the object data from storage.
func (s *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) {
@ -204,9 +218,9 @@ func (s *ObjectStorage) packfile(idx idxfile.Index, pack plumbing.Hash) (*packfi
var p *packfile.Packfile
if s.objectCache != nil {
p = packfile.NewPackfileWithCache(idx, s.dir.Fs(), f, s.objectCache)
p = packfile.NewPackfileWithCache(idx, s.dir.Fs(), f, s.objectCache, s.options.LargeObjectThreshold)
} else {
p = packfile.NewPackfile(idx, s.dir.Fs(), f)
p = packfile.NewPackfile(idx, s.dir.Fs(), f, s.options.LargeObjectThreshold)
}
return p, s.storePackfileInCache(pack, p)
@ -389,7 +403,6 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
return cacheObj, nil
}
obj = s.NewEncodedObject()
r, err := objfile.NewReader(f)
if err != nil {
return nil, err
@ -402,6 +415,13 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
return nil, err
}
if s.options.LargeObjectThreshold > 0 && size > s.options.LargeObjectThreshold {
obj = dotgit.NewEncodedObject(s.dir, h, t, size)
return obj, nil
}
obj = s.NewEncodedObject()
obj.SetType(t)
obj.SetSize(size)
w, err := obj.Writer()
@ -411,12 +431,23 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
defer ioutil.CheckClose(w, &err)
bufp := copyBufferPool.Get().(*[]byte)
buf := *bufp
_, err = io.CopyBuffer(w, r, buf)
copyBufferPool.Put(bufp)
s.objectCache.Put(obj)
_, err = io.Copy(w, r)
return obj, err
}
var copyBufferPool = sync.Pool{
New: func() interface{} {
b := make([]byte, 32*1024)
return &b
},
}
// Get returns the object with the given hash, by searching for it in
// the packfile.
func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) (
@ -519,14 +550,21 @@ func (s *ObjectStorage) findObjectInPackfile(h plumbing.Hash) (plumbing.Hash, pl
return plumbing.ZeroHash, plumbing.ZeroHash, -1
}
// HashesWithPrefix returns all objects with a hash that starts with a prefix by searching for
// them in the packfile and the git object directories.
func (s *ObjectStorage) HashesWithPrefix(prefix []byte) ([]plumbing.Hash, error) {
hashes, err := s.dir.ObjectsWithPrefix(prefix)
if err != nil {
return nil, err
}
seen := hashListAsMap(hashes)
// TODO: This could be faster with some idxfile changes,
// or diving into the packfile.
if err := s.requireIndex(); err != nil {
return nil, err
}
for _, index := range s.index {
ei, err := index.Entries()
if err != nil {
@ -540,6 +578,9 @@ func (s *ObjectStorage) HashesWithPrefix(prefix []byte) ([]plumbing.Hash, error)
return nil, err
}
if bytes.HasPrefix(e.Hash[:], prefix) {
if _, ok := seen[e.Hash]; ok {
continue
}
hashes = append(hashes, e.Hash)
}
}
@ -595,6 +636,7 @@ func (s *ObjectStorage) buildPackfileIters(
return newPackfileIter(
s.dir.Fs(), pack, t, seen, s.index[h],
s.objectCache, s.options.KeepDescriptors,
s.options.LargeObjectThreshold,
)
},
}, nil
@ -684,6 +726,7 @@ func NewPackfileIter(
idxFile billy.File,
t plumbing.ObjectType,
keepPack bool,
largeObjectThreshold int64,
) (storer.EncodedObjectIter, error) {
idx := idxfile.NewMemoryIndex()
if err := idxfile.NewDecoder(idxFile).Decode(idx); err != nil {
@ -695,7 +738,7 @@ func NewPackfileIter(
}
seen := make(map[plumbing.Hash]struct{})
return newPackfileIter(fs, f, t, seen, idx, nil, keepPack)
return newPackfileIter(fs, f, t, seen, idx, nil, keepPack, largeObjectThreshold)
}
func newPackfileIter(
@ -706,12 +749,13 @@ func newPackfileIter(
index idxfile.Index,
cache cache.Object,
keepPack bool,
largeObjectThreshold int64,
) (storer.EncodedObjectIter, error) {
var p *packfile.Packfile
if cache != nil {
p = packfile.NewPackfileWithCache(index, fs, f, cache)
p = packfile.NewPackfileWithCache(index, fs, f, cache, largeObjectThreshold)
} else {
p = packfile.NewPackfile(index, fs, f)
p = packfile.NewPackfile(index, fs, f, largeObjectThreshold)
}
iter, err := p.GetByType(t)

View File

@ -34,7 +34,7 @@ func (s *ShallowStorage) SetShallow(commits []plumbing.Hash) error {
return err
}
// Shallow return the shallow commits reading from shallo file from .git
// Shallow returns the shallow commits reading from shallo file from .git
func (s *ShallowStorage) Shallow() ([]plumbing.Hash, error) {
f, err := s.dir.Shallow()
if f == nil || err != nil {

View File

@ -34,6 +34,13 @@ type Options struct {
// MaxOpenDescriptors is the max number of file descriptors to keep
// open. If KeepDescriptors is true, all file descriptors will remain open.
MaxOpenDescriptors int
// LargeObjectThreshold maximum object size (in bytes) that will be read in to memory.
// If left unset or set to 0 there is no limit
LargeObjectThreshold int64
// AlternatesFS provides the billy filesystem to be used for Git Alternates.
// If none is provided, it falls back to using the underlying instance used for
// DotGit.
AlternatesFS billy.Filesystem
}
// NewStorage returns a new Storage backed by a given `fs.Filesystem` and cache.
@ -46,6 +53,7 @@ func NewStorage(fs billy.Filesystem, cache cache.Object) *Storage {
func NewStorageWithOptions(fs billy.Filesystem, cache cache.Object, ops Options) *Storage {
dirOps := dotgit.Options{
ExclusiveAccess: ops.ExclusiveAccess,
AlternatesFS: ops.AlternatesFS,
}
dir := dotgit.NewWithOptions(fs, dirOps)
@ -71,3 +79,7 @@ func (s *Storage) Filesystem() billy.Filesystem {
func (s *Storage) Init() error {
return s.dir.Initialize()
}
func (s *Storage) AddAlternate(remote string) error {
return s.dir.AddAlternate(remote)
}

View File

@ -193,7 +193,7 @@ func (o *ObjectStorage) DeleteOldObjectPackAndIndex(plumbing.Hash, time.Time) er
return nil
}
var errNotSupported = fmt.Errorf("Not supported")
var errNotSupported = fmt.Errorf("not supported")
func (o *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) {
return time.Time{}, errNotSupported
@ -202,6 +202,10 @@ func (o *ObjectStorage) DeleteLooseObject(plumbing.Hash) error {
return errNotSupported
}
func (o *ObjectStorage) AddAlternate(remote string) error {
return errNotSupported
}
type TxObjectStorage struct {
Storage *ObjectStorage
Objects map[plumbing.Hash]plumbing.EncodedObject