summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2019-02-16 21:38:07 -0500
committerLuke Shumaker <lukeshu@lukeshu.com>2019-02-16 21:38:07 -0500
commit9d5785c88add1342dbaa9f2cfa4596f21b945354 (patch)
tree3eb801934860b025070c22d4d2905e47b22cb463
parent97c4b4053ab187a75a1a79854df2941522cdab09 (diff)
more
-rw-r--r--fi-prune-empty/main.go348
1 files changed, 177 insertions, 171 deletions
diff --git a/fi-prune-empty/main.go b/fi-prune-empty/main.go
index d45cc6a..e56250c 100644
--- a/fi-prune-empty/main.go
+++ b/fi-prune-empty/main.go
@@ -20,13 +20,15 @@ package main
import (
"fmt"
"io"
- "strconv"
"os"
"sort"
+ "strconv"
"strings"
"git.lukeshu.com/go/libfastimport"
"github.com/pkg/errors"
+
+ "git.parabola.nu/~lukeshu/fastimport-go-utils/fiutil"
)
func usage(w io.Writer) {
@@ -43,7 +45,8 @@ func main() {
frontend := libfastimport.NewFrontend(os.Stdin, os.Stdin, nil)
backend := libfastimport.NewBackend(os.Stdout, os.Stdout, nil)
- if err := prune(backend, frontend); err != nil {
+ filter := NewPruneEmpty(backend)
+ if err := fiutil.RunHandler(frontend, filter); err != nil {
fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err)
os.Exit(1)
}
@@ -67,185 +70,188 @@ func TreesEqual(a, b Tree) bool {
return true
}
-func prune(backend *libfastimport.Backend, frontend *libfastimport.Frontend) error {
- replace := map[string]string{}
- fixupMark := func(m string) string {
- if r, ok := replace[m]; ok {
- return r
+type PruneEmpty struct {
+ backend *libfastimport.Backend
+
+ replace map[string]string
+
+ ancestors map[string]map[string]struct{}
+ trees map[string]Tree
+
+ commitMeta libfastimport.CmdCommit
+ commitFile Tree
+}
+
+func NewPruneEmpty(backend *libfastimport.Backend) *PruneEmpty {
+ return &PruneEmpty{
+ backend: backend,
+
+ replace: map[string]string{},
+
+ ancestors: map[string]map[string]struct{}{},
+ trees: map[string]Tree{},
+
+ commitMeta: libfastimport.CmdCommit{},
+ commitFile: Tree{},
+ }
+}
+
+func (h *PruneEmpty) fixupMark(m string) string {
+ if r, ok := h.replace[m]; ok {
+ return r
+ }
+ return m
+}
+
+func (h *PruneEmpty) addCommit(c string, parents []string, tree Tree) {
+ cAncestors := map[string]struct{}{}
+ for _, parent := range parents {
+ parent = h.fixupMark(parent)
+ cAncestors[parent] = struct{}{}
+ for ancestor := range h.ancestors[parent] {
+ cAncestors[ancestor] = struct{}{}
}
- return m
- }
-
- ancestors := map[string]map[string]struct{}{}
- trees := map[string]Tree{}
- addCommit := func(c string, parents []string, tree Tree) {
- cAncestors := map[string]struct{}{}
- for _, parent := range parents {
- parent = fixupMark(parent)
- cAncestors[parent] = struct{}{}
- for ancestor := range ancestors[parent] {
- cAncestors[ancestor] = struct{}{}
- }
+ }
+ h.ancestors[c] = cAncestors
+ h.trees[c] = tree
+}
+
+// subsumedBy(c, commits) determines if commit 'c' is an
+// ancestor of any of the commits given in 'commits'.
+func (h *PruneEmpty) subsumedBy(c string, commits []string) bool {
+ for _, c2 := range commits {
+ c2 = h.fixupMark(c2)
+ if c2 == c {
+ continue
}
- ancestors[c] = cAncestors
- trees[c] = tree
- }
- // subsumedBy(c, commits) determines if commit 'c' is an
- // ancestor of any of the commits given in 'commits'.
- subsumedBy := func(c string, commits []string) bool {
- for _, c2 := range commits {
- c2 = fixupMark(c2)
- if c2 == c {
- continue
- }
- if _, ok := ancestors[c2][c]; ok {
- return true
- }
+ if _, ok := h.ancestors[c2][c]; ok {
+ return true
}
- return false
}
- pruneParents := func(commits []string) []string {
- var ret []string
- for _, c := range commits {
- c = fixupMark(c)
- if !subsumedBy(c, commits) {
- ret = append(ret, c)
- }
+ return false
+}
+func (h *PruneEmpty) pruneParents(commits []string) []string {
+ var ret []string
+ for _, c := range commits {
+ c = h.fixupMark(c)
+ if !h.subsumedBy(c, commits) {
+ ret = append(ret, c)
}
- return ret
}
+ return ret
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// commit //////////////////////////////////////////////////////////////////////
+func (h *PruneEmpty) CmdCommit(cmd libfastimport.CmdCommit) error {
+ h.commitMeta = cmd
+ h.commitFile = Tree{}
+ return nil
+}
+func (h *PruneEmpty) CmdCommitEnd(cmd libfastimport.CmdCommitEnd) error {
+ mark := fmt.Sprintf(":%d", h.commitMeta.Mark)
+ parents := h.pruneParents(append([]string{h.commitMeta.From}, h.commitMeta.Merge...))
+ sort.Stable(h.commitFile)
+
+ // decide whether to skip this commit
+ if len(parents) == 1 && TreesEqual(h.trees[parents[0]], h.commitFile) {
+ h.replace[mark] = parents[0]
+ return nil
+ }
+
+ // remember the commit
+ h.addCommit(mark, parents, h.commitFile)
- commitMeta := libfastimport.CmdCommit{}
- commitFile := Tree{}
- for {
- cmd, err := frontend.ReadCmd()
- if err != nil {
- if err == io.EOF {
- break
- }
+ // emit the commit
+ h.commitMeta.From = parents[0]
+ h.commitMeta.Merge = parents[1:]
+ if err := h.backend.Do(h.commitMeta); err != nil {
+ return err
+ }
+ if err := h.backend.Do(libfastimport.FileDeleteAll{}); err != nil {
+ return err
+ }
+ for _, file := range h.commitFile {
+ if err := h.backend.Do(file); err != nil {
return err
}
+ }
+
+ return nil
+}
+
+// file commands ///////////////////////////////////////////////////////////////
+func (h *PruneEmpty) FileModify(cmd libfastimport.FileModify) error {
+ h.commitFile = append(h.commitFile, cmd)
+ return nil
+}
+func (h *PruneEmpty) FileModifyInline(cmd libfastimport.FileModifyInline) error {
+ return errors.New("unexpected inline \"filemodify\" command; must use a mark")
+}
+func (h *PruneEmpty) FileCopy(cmd libfastimport.FileCopy) error {
+ return errors.New("unexpected \"filecopy\" command: this filter requires --full-tree")
+}
+func (h *PruneEmpty) FileRename(cmd libfastimport.FileRename) error {
+ return errors.New("unexpected \"filerename\" command: this filter requires --full-tree")
+}
+func (h *PruneEmpty) FileDelete(cmd libfastimport.FileDelete) error {
+ return errors.New("unexpected \"filedelete\" command: this filter requires --full-tree")
+}
+func (h *PruneEmpty) FileDeleteAll(cmd libfastimport.FileDeleteAll) error {
+ // do nothing
+ return nil
+}
+
+// note commands ///////////////////////////////////////////////////////////////
+func (h *PruneEmpty) NoteModify(cmd libfastimport.NoteModify) error {
+ return errors.Errorf("unsupported (but known) command %T", cmd)
+}
+func (h *PruneEmpty) NoteModifyInline(cmd libfastimport.NoteModifyInline) error {
+ return errors.Errorf("unsupported (but known) command %T", cmd)
+}
+
+// other commands //////////////////////////////////////////////////////////////
+func (h *PruneEmpty) CmdBlob(cmd libfastimport.CmdBlob) error { return h.backend.Do(cmd) }
+func (h *PruneEmpty) CmdCheckpoint(cmd libfastimport.CmdCheckpoint) error { return h.backend.Do(cmd) }
+func (h *PruneEmpty) CmdComment(cmd libfastimport.CmdComment) error { return h.backend.Do(cmd) }
+func (h *PruneEmpty) CmdDone(cmd libfastimport.CmdDone) error { return h.backend.Do(cmd) }
+func (h *PruneEmpty) CmdOption(cmd libfastimport.CmdOption) error { return h.backend.Do(cmd) }
+func (h *PruneEmpty) CmdProgress(cmd libfastimport.CmdProgress) error { return h.backend.Do(cmd) }
+func (h *PruneEmpty) CmdReset(cmd libfastimport.CmdReset) error { return h.backend.Do(cmd) }
+func (h *PruneEmpty) CmdTag(cmd libfastimport.CmdTag) error { return h.backend.Do(cmd) }
- switch cmdt := cmd.(type) {
-
- // commit //////////////////////////////////////////////
- case libfastimport.CmdCommit:
- commitMeta = cmdt
- commitFile = Tree{}
- case libfastimport.CmdCommitEnd:
- mark := fmt.Sprintf(":%d", commitMeta.Mark)
- parents := pruneParents(append([]string{commitMeta.From}, commitMeta.Merge...))
- sort.Stable(commitFile)
-
- // decide whether to skip this commit
- if len(parents) == 1 && TreesEqual(trees[parents[0]], commitFile) {
- replace[mark] = parents[0]
- continue
- }
-
- // remember the commit
- addCommit(mark, parents, commitFile)
-
- // emit the commit
- commitMeta.From = parents[0]
- commitMeta.Merge = parents[1:]
- if err := backend.Do(commitMeta); err != nil {
- return err
- }
- if err := backend.Do(libfastimport.FileDeleteAll{}); err != nil {
- return err
- }
- for _, file := range commitFile {
- if err := backend.Do(file); err != nil {
- return err
- }
- }
-
- // file commands ///////////////////////////////////////
- case libfastimport.FileModify:
- commitFile = append(commitFile, cmdt)
- case libfastimport.FileModifyInline:
- return errors.New("unexpected inline \"filemodify\" command; must use a mark")
- case libfastimport.FileCopy:
- return errors.New("unexpected \"filecopy\" command: this filter requires --full-tree")
- case libfastimport.FileRename:
- return errors.New("unexpected \"filerename\" command: this filter requires --full-tree")
- case libfastimport.FileDelete:
- return errors.New("unexpected \"filedelete\" command: this filter requires --full-tree")
- case libfastimport.FileDeleteAll:
- // do nothing
-
- // note commands ///////////////////////////////////////
- case libfastimport.NoteModify, libfastimport.NoteModifyInline:
- return errors.Errorf("unsupported (but known) command %T", cmd)
-
- // regular commands ////////////////////////////////////
- case libfastimport.CmdBlob, libfastimport.CmdCheckpoint, libfastimport.CmdComment, libfastimport.CmdDone, libfastimport.CmdProgress, libfastimport.CmdReset, libfastimport.CmdTag:
- if err := backend.Do(cmd); err != nil {
- return err
- }
-
- // special commands ////////////////////////////////////
- case libfastimport.CmdOption:
- if err := backend.Do(cmd); err != nil {
- return err
- }
- case libfastimport.CmdFeature:
- switch cmdt.Feature {
- case "date-format":
- if cmdt.Argument != "raw" {
- return errors.Errorf("date-format=%q: only supports the %q format", cmdt.Argument, "raw")
- }
- if err := backend.Do(cmd); err != nil {
- return err
- }
- case "export-marks", "relative-marks", "no-relative-marks", "force", "import-marks", "import-marks-if-exists", "get-mark", "cat-blob", "ls":
- if err := backend.Do(cmd); err != nil {
- return err
- }
- case "notes":
- return errors.Errorf("unsupported (but known) command %T", cmd)
- case "done":
- if err := backend.Do(cmd); err != nil {
- return err
- }
- default:
- return errors.Errorf("unknown feature %q", cmdt.Feature)
- }
- case libfastimport.CmdCatBlob:
- if strings.HasPrefix(cmdt.DataRef, ":") {
- cmdt.DataRef = fixupMark(cmdt.DataRef)
- }
- sha1, data, err := backend.CatBlob(cmdt)
- if err != nil {
- return err
- }
- if err := frontend.RespondCatBlob(sha1, data); err != nil {
- return err
- }
- case libfastimport.CmdGetMark:
- cmdt.Mark, _ = strconv.Atoi(fixupMark(fmt.Sprintf(":%d", cmdt.Mark))[1:])
- sha1, err := backend.GetMark(cmdt)
- if err != nil {
- return err
- }
- if err := frontend.RespondGetMark(sha1); err != nil {
- return err
- }
- case libfastimport.CmdLs:
- if strings.HasPrefix(cmdt.DataRef, ":") {
- cmdt.DataRef = fixupMark(cmdt.DataRef)
- }
- mode, dataref, path, err := backend.Ls(cmdt)
- if err != nil {
- return err
- }
- if err := frontend.RespondLs(mode, dataref, path); err != nil {
- return err
- }
- default:
- return errors.Errorf("unexpected command: %[1]T(%#[1]v)", cmd)
+func (h *PruneEmpty) CmdCatBlob(cmd libfastimport.CmdCatBlob) (sha1 string, data string, err error) {
+ if strings.HasPrefix(cmd.DataRef, ":") {
+ cmd.DataRef = h.fixupMark(cmd.DataRef)
+ }
+ return h.backend.CatBlob(cmd)
+}
+func (h *PruneEmpty) CmdGetMark(cmd libfastimport.CmdGetMark) (sha1 string, err error) {
+ cmd.Mark, _ = strconv.Atoi(h.fixupMark(fmt.Sprintf(":%d", cmd.Mark))[1:])
+ return h.backend.GetMark(cmd)
+}
+func (h *PruneEmpty) CmdLs(cmd libfastimport.CmdLs) (mode libfastimport.Mode, dataref string, path libfastimport.Path, err error) {
+ if strings.HasPrefix(cmd.DataRef, ":") {
+ cmd.DataRef = h.fixupMark(cmd.DataRef)
+ }
+ return h.backend.Ls(cmd)
+}
+
+func (h *PruneEmpty) CmdFeature(cmd libfastimport.CmdFeature) error {
+ switch cmd.Feature {
+ case "date-format":
+ if cmd.Argument != "raw" {
+ return errors.Errorf("date-format=%q: only supports the %q format", cmd.Argument, "raw")
}
+ return h.backend.Do(cmd)
+ case "export-marks", "relative-marks", "no-relative-marks", "force", "import-marks", "import-marks-if-exists", "get-mark", "cat-blob", "ls":
+ return h.backend.Do(cmd)
+ case "notes":
+ return errors.Errorf("unsupported (but known) command %T", cmd)
+ case "done":
+ return h.backend.Do(cmd)
+ default:
+ return errors.Errorf("unknown feature %q", cmd.Feature)
}
- return nil
}