diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2019-02-16 21:38:07 -0500 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2019-02-16 21:38:07 -0500 |
commit | 9d5785c88add1342dbaa9f2cfa4596f21b945354 (patch) | |
tree | 3eb801934860b025070c22d4d2905e47b22cb463 | |
parent | 97c4b4053ab187a75a1a79854df2941522cdab09 (diff) |
more
-rw-r--r-- | fi-prune-empty/main.go | 348 |
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 } |