aboutsummaryrefslogtreecommitdiff
path: root/pkg/git/git.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/git/git.go')
-rw-r--r--pkg/git/git.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/pkg/git/git.go b/pkg/git/git.go
index b725cd8..591fafb 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -1,9 +1,13 @@
package git
import (
+ "archive/tar"
"errors"
"fmt"
"io"
+ "io/fs"
+ "path"
+ "time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
@@ -26,6 +30,13 @@ type (
// this is setRef when ref is setRef
setRef bool
}
+ infoWrapper struct {
+ name string
+ size int64
+ mode fs.FileMode
+ modTime time.Time
+ isDir bool
+ }
)
func OpenRepository(dir string) (*GitRepository, error) {
@@ -213,3 +224,110 @@ func (g *GitRepository) FileContent(path string) (string, error) {
return "Binary file", nil
}
}
+
+func (g *GitRepository) WriteTar(w io.Writer, prefix string) error {
+ tw := tar.NewWriter(w)
+ defer tw.Close()
+
+ tree, err := g.Tree("")
+ if err != nil {
+ return err
+ }
+
+ walker := object.NewTreeWalker(tree, true, nil)
+ defer walker.Close()
+
+ name, entry, err := walker.Next()
+ for ; err == nil; name, entry, err = walker.Next() {
+ info, err := newInfoWrapper(name, prefix, &entry, tree)
+ if err != nil {
+ return err
+ }
+
+ header, err := tar.FileInfoHeader(info, "")
+ if err != nil {
+ return err
+ }
+
+ err = tw.WriteHeader(header)
+ if err != nil {
+ return err
+ }
+
+ if !info.IsDir() {
+ c, err := g.FileContent(name)
+ if err != nil {
+ return err
+ }
+
+ _, err = tw.Write([]byte(c))
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func newInfoWrapper(
+ filename string,
+ prefix string,
+ entry *object.TreeEntry,
+ tree *object.Tree,
+) (*infoWrapper, error) {
+ var (
+ size int64
+ mode fs.FileMode
+ isDir bool
+ )
+
+ if entry.Mode.IsFile() {
+ file, err := tree.TreeEntryFile(entry)
+ if err != nil {
+ return nil, err
+ }
+ mode = fs.FileMode(file.Mode)
+
+ size, err = tree.Size(filename)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ isDir = true
+ mode = fs.ModeDir | fs.ModePerm
+ }
+
+ fullname := path.Join(prefix, filename)
+ return &infoWrapper{
+ name: fullname,
+ size: size,
+ mode: mode,
+ modTime: time.Unix(0, 0),
+ isDir: isDir,
+ }, nil
+}
+
+func (i *infoWrapper) Name() string {
+ return i.name
+}
+
+func (i *infoWrapper) Size() int64 {
+ return i.size
+}
+
+func (i *infoWrapper) Mode() fs.FileMode {
+ return i.mode
+}
+
+func (i *infoWrapper) ModTime() time.Time {
+ return i.modTime
+}
+
+func (i *infoWrapper) IsDir() bool {
+ return i.isDir
+}
+
+func (i *infoWrapper) Sys() any {
+ return nil
+}