diff options
author | Gabriel A. Giovanini <mail@gabrielgio.me> | 2024-06-23 15:19:15 +0200 |
---|---|---|
committer | Anirudh Oppiliappan <x@icyphox.sh> | 2024-06-29 11:16:38 +0300 |
commit | 86b2bf47ff930778bd73cce1cda916ffad41518b (patch) | |
tree | c4e1854d4793adf2a3819fb08d408bde5808630d /git | |
parent | nix: init flake (diff) | |
download | legitrx-86b2bf47ff930778bd73cce1cda916ffad41518b.tar.gz legitrx-86b2bf47ff930778bd73cce1cda916ffad41518b.tar.zst legitrx-86b2bf47ff930778bd73cce1cda916ffad41518b.zip |
git: Add function to generate tar file from repo
Diffstat (limited to 'git')
-rw-r--r-- | git/git.go | 136 |
1 files changed, 136 insertions, 0 deletions
@@ -1,8 +1,13 @@ package git import ( + "archive/tar" "fmt" + "io" + "io/fs" + "path" "sort" + "time" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -16,6 +21,16 @@ type GitRepo struct { type TagList []*object.Tag +// infoWrapper wraps the property of a TreeEntry so it can export fs.FileInfo +// to tar WriteHeader +type infoWrapper struct { + name string + size int64 + mode fs.FileMode + modTime time.Time + isDir bool +} + func (self TagList) Len() int { return len(self) } @@ -154,3 +169,124 @@ func (g *GitRepo) FindMainBranch(branches []string) (string, error) { } return "", fmt.Errorf("unable to find main branch") } + +// WriteTar writes itself from a tree into a binary tar file format. +// prefix is root folder to be appended. +func (g *GitRepo) WriteTar(w io.Writer, prefix string) error { + tw := tar.NewWriter(w) + defer tw.Close() + + c, err := g.r.CommitObject(g.h) + if err != nil { + return fmt.Errorf("commit object: %w", err) + } + + tree, err := c.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() { + file, err := tree.File(name) + if err != nil { + return err + } + + reader, err := file.Blob.Reader() + if err != nil { + return err + } + + _, err = io.Copy(tw, reader) + if err != nil { + reader.Close() + return err + } + reader.Close() + } + } + + return nil +} + +func newInfoWrapper( + name 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(name) + if err != nil { + return nil, err + } + } else { + isDir = true + mode = fs.ModeDir | fs.ModePerm + } + + fullname := path.Join(prefix, name) + 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 +} |