From f13250e6a926640c4d0ee858f84fcf8036d612aa Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 22 Nov 2017 14:47:56 -0500 Subject: ahhh --- backend.go | 22 ++++++- cmd.go | 19 ++++++ cmd_command.go | 138 ++++++++++++++++++++++++++++++++++++++ cmd_comment.go | 54 +++++++++++++++ cmd_commit.go | 89 +++++++++++++++++++++++++ commands.go | 181 -------------------------------------------------- fileactions.go | 94 -------------------------- frontend.go | 205 +++++++++++++++++++++++---------------------------------- util.go | 49 ++++++++++++++ 9 files changed, 452 insertions(+), 399 deletions(-) create mode 100644 cmd.go create mode 100644 cmd_command.go create mode 100644 cmd_comment.go create mode 100644 cmd_commit.go delete mode 100644 commands.go delete mode 100644 fileactions.go create mode 100644 util.go diff --git a/backend.go b/backend.go index fc67d82..72242a1 100644 --- a/backend.go +++ b/backend.go @@ -2,6 +2,7 @@ package libfastimport import ( "bufio" + "fmt" "io" "git.lukeshu.com/go/libfastimport/textproto" @@ -15,6 +16,8 @@ type Backend struct { fiw *textproto.FIWriter cbr *textproto.CatBlobReader + inCommit bool + err error onErr func(error) error } @@ -45,12 +48,27 @@ func NewBackend(fastImport io.WriteCloser, catBlob io.Reader, onErr func(error) return ret } +// will panic if Cmd is a type that may only be used in a commit but +// we aren't in a commit. func (b *Backend) Do(cmd Cmd) error { if b.err == nil { return b.err } - err := cmd.fiWriteCmd(b.fiw) + switch cmd.fiCmdClass() { + case cmdClassCommand: + _, b.inCommit = cmd.(CmdCommit) + case cmdClassCommit: + if !b.inCommit { + panic(fmt.Errorf("Cannot issue commit sub-command outside of a commit: %[1]T(%#[1]v)", cmd)) + } + case cmdClassComment: + /* do nothing */ + default: + panic(fmt.Errorf("invalid cmdClass: %d", cmd.fiCmdClass())) + } + + err := cmd.fiCmdWrite(b.fiw) if err != nil { return b.onErr(err) } @@ -59,7 +77,7 @@ func (b *Backend) Do(cmd Cmd) error { return b.onErr(err) } - if _, ok := cmd.(CmdDone); ok { + if _, isDone := cmd.(CmdDone); isDone { return b.onErr(nil) } diff --git a/cmd.go b/cmd.go new file mode 100644 index 0000000..7ba40e0 --- /dev/null +++ b/cmd.go @@ -0,0 +1,19 @@ +package libfastimport + +import ( + "git.lukeshu.com/go/libfastimport/textproto" +) + +type cmdClass int + +const ( + cmdClassCommand cmdClass = 1 // may be a top-level command + cmdClassCommit cmdClass = 2 // may be used within in a commit + + cmdClassComment cmdClass = cmdClassCommand | cmdClassCommit +) + +type Cmd interface { + fiCmdWrite(*textproto.FIWriter) error + fiCmdClass() cmdClass +} diff --git a/cmd_command.go b/cmd_command.go new file mode 100644 index 0000000..a1d1a41 --- /dev/null +++ b/cmd_command.go @@ -0,0 +1,138 @@ +package libfastimport + +import ( + "git.lukeshu.com/go/libfastimport/textproto" +) + +type CmdCommit struct { + Ref string + Mark int // optional; < 1 for non-use + Author *textproto.Ident + Committer textproto.Ident + Msg string + From string + Merge []string +} + +func (c CmdCommit) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdCommit) fiCmdWrite(fiw *textproto.FIWriter) error { + ez := &ezfiw{fiw: fiw} + + ez.WriteLine("commit", c.Ref) + if c.Mark > 0 { + ez.WriteMark(c.Mark) + } + if c.Author != nil { + ez.WriteLine("author", *c.Author) + } + ez.WriteLine("committer", c.Committer) + ez.WriteData(c.Msg) + if c.From != "" { + ez.WriteLine("from", c.From) + } + for _, merge := range c.Merge { + ez.WriteLine("merge", merge) + } + + return ez.err +} + +type CmdTag struct { + RefName string + CommitIsh string + Tagger textproto.Ident + Data string +} + +func (c CmdTag) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdTag) fiCmdWrite(fiw *textproto.FIWriter) error { + ez := &ezfiw{fiw: fiw} + + ez.WriteLine("tag", c.RefName) + ez.WriteLine("from", c.CommitIsh) + ez.WriteLine("tagger", c.Tagger) + ez.WriteData(c.Data) + + return ez.err +} + +type CmdReset struct { + RefName string + CommitIsh string // optional +} + +func (c CmdReset) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdReset) fiCmdWrite(fiw *textproto.FIWriter) error { + ez := &ezfiw{fiw: fiw} + + ez.WriteLine("reset", c.RefName) + if c.CommitIsh != "" { + ez.WriteLine("from", c.CommitIsh) + } + + return ez.err +} + +type CmdBlob struct { + Mark int + Data string +} + +func (c CmdBlob) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdBlob) fiCmdWrite(fiw *textproto.FIWriter) error { + ez := &ezfiw{fiw: fiw} + + ez.WriteLine("blob") + if c.Mark > 0 { + ez.WriteMark(c.Mark) + } + ez.WriteData(c.Data) + + return ez.err +} + +type CmdCheckpoint struct{} + +func (c CmdCheckpoint) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdCheckpoint) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("checkpoint") +} + +type CmdProgress struct { + Str string +} + +func (c CmdProgress) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdProgress) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("progress", c.Str) +} + +type CmdFeature struct { + Feature string + Argument string +} + +func (c CmdFeature) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdFeature) fiCmdWrite(fiw *textproto.FIWriter) error { + if c.Argument != "" { + return fiw.WriteLine("feature", c.Feature+"="+c.Argument) + } else { + return fiw.WriteLine("feature", c.Feature) + } +} + +type CmdOption struct { + Option string +} + +func (c CmdOption) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdOption) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("option", c.Option) +} + +type CmdDone struct{} + +func (c CmdDone) fiCmdClass() cmdClass { return cmdClassCommand } +func (c CmdDone) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("done") +} diff --git a/cmd_comment.go b/cmd_comment.go new file mode 100644 index 0000000..d783ecc --- /dev/null +++ b/cmd_comment.go @@ -0,0 +1,54 @@ +package libfastimport + +import ( + "strconv" + + "git.lukeshu.com/go/libfastimport/textproto" +) + +type CmdComment struct { + Comment string +} + +func (c CmdComment) fiCmdClass() cmdClass { return cmdClassComment } +func (c CmdComment) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("#" + c.Comment) +} + +type CmdGetMark struct { + Mark int +} + +func (c CmdGetMark) fiCmdClass() cmdClass { return cmdClassComment } +func (c CmdGetMark) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("get-mark", ":"+strconv.Itoa(c.Mark)) +} + +type CmdCatBlob struct { + DataRef string +} + +func (c CmdCatBlob) fiCmdClass() cmdClass { return cmdClassComment } +func (c CmdCatBlob) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("cat-blob", c.DataRef) +} + +type CmdLs struct { + DataRef string // optional if inside of a commit + Path textproto.Path +} + +func (c CmdLs) fiCmdClass() cmdClass { + if c.DataRef == "" { + return cmdClassCommit + } + return cmdClassComment +} +func (c CmdLs) fiCmdWrite(fiw *textproto.FIWriter) error { + if c.DataRef == "" { + return fiw.WriteLine("ls", c.Path) + } else { + return fiw.WriteLine("ls", c.DataRef, c.Path) + } +} + diff --git a/cmd_commit.go b/cmd_commit.go new file mode 100644 index 0000000..5c882f8 --- /dev/null +++ b/cmd_commit.go @@ -0,0 +1,89 @@ +package libfastimport + +import ( + "git.lukeshu.com/go/libfastimport/textproto" +) + +type FileModify struct { + Mode textproto.Mode + Path textproto.Path + DataRef string +} + +func (o FileModify) fiCmdClass() cmdClass { return cmdClassCommit } +func (o FileModify) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("M", o.Mode, o.DataRef, o.Path) +} + +type FileModifyInline struct { + Mode textproto.Mode + Path textproto.Path + Data string +} + +func (o FileModifyInline) fiCmdClass() cmdClass { return cmdClassCommit } +func (o FileModifyInline) fiCmdWrite(fiw *textproto.FIWriter) error { + ez := &ezfiw{fiw: fiw} + ez.WriteLine("M", o.Mode, "inline", o.Path) + ez.WriteData(o.Data) + return ez.err +} + +type FileDelete struct { + Path textproto.Path +} + +func (o FileDelete) fiCmdClass() cmdClass { return cmdClassCommit } +func (o FileDelete) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("D", o.Path) +} + +type FileCopy struct { + Src textproto.Path + Dst textproto.Path +} + +func (o FileCopy) fiCmdClass() cmdClass { return cmdClassCommit } +func (o FileCopy) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("C", o.Src, o.Dst) +} + +type FileRename struct { + Src string + Dst string +} + +func (o FileRename) fiCmdClass() cmdClass { return cmdClassCommit } +func (o FileRename) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("R", o.Src, o.Dst) +} + +type FileDeleteAll struct{} + +func (o FileDeleteAll) fiCmdClass() cmdClass { return cmdClassCommit } +func (o FileDeleteAll) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("deleteall") +} + +type NoteModify struct { + CommitIsh string + DataRef string +} + +func (o NoteModify) fiCmdClass() cmdClass { return cmdClassCommit } +func (o NoteModify) fiCmdWrite(fiw *textproto.FIWriter) error { + return fiw.WriteLine("N", o.DataRef, o.CommitIsh) +} + +type NoteModifyInline struct { + CommitIsh string + Data string +} + +func (o NoteModifyInline) fiCmdClass() cmdClass { return cmdClassCommit } +func (o NoteModifyInline) fiCmdWrite(fiw *textproto.FIWriter) error { + ez := &ezfiw{fiw: fiw} + ez.WriteLine("N", "inline", o.CommitIsh) + ez.WriteData(o.Data) + return ez.err +} diff --git a/commands.go b/commands.go deleted file mode 100644 index 7e7c4f3..0000000 --- a/commands.go +++ /dev/null @@ -1,181 +0,0 @@ -package libfastimport - -import ( - "strconv" - - "git.lukeshu.com/go/libfastimport/textproto" -) - -type Cmd interface { - fiWriteCmd(*textproto.FIWriter) error -} - -type CmdCommit struct { - Ref string - Mark int // optional; < 1 for non-use - Author *textproto.Ident - Committer textproto.Ident - Msg string - From string - Merge []string - Tree []FileAction -} - -func (c CmdCommit) fiWriteCmd(fiw *textproto.FIWriter) error { - ez := &ezfiw{fiw: fiw} - - ez.WriteLine("commit", c.Ref) - if c.Mark > 0 { - ez.WriteMark(c.Mark) - } - if c.Author != nil { - ez.WriteLine("author", *c.Author) - } - ez.WriteLine("committer", c.Committer) - ez.WriteData(c.Msg) - if c.From != "" { - ez.WriteLine("from", c.From) - } - for _, merge := range c.Merge { - ez.WriteLine("merge", merge) - } - - if ez.err != nil { - return ez.err - } - - for _, action := range c.Tree { - err := action.fiWriteFA(fiw) - if err != nil { - return err - } - } - - return nil -} - -type CmdTag struct { - RefName string - CommitIsh string - Tagger textproto.Ident - Data string -} - -func (c CmdTag) fiWriteCmd(fiw *textproto.FIWriter) error { - ez := &ezfiw{fiw: fiw} - - ez.WriteLine("tag", c.RefName) - ez.WriteLine("from", c.CommitIsh) - ez.WriteLine("tagger", c.Tagger) - ez.WriteData(c.Data) - - return ez.err -} - -type CmdReset struct { - RefName string - CommitIsh string // optional -} - -func (c CmdReset) fiWriteCmd(fiw *textproto.FIWriter) error { - ez := &ezfiw{fiw: fiw} - - ez.WriteLine("reset", c.RefName) - if c.CommitIsh != "" { - ez.WriteLine("from", c.CommitIsh) - } - - return ez.err -} - -type CmdBlob struct { - Mark int - Data string -} - -func (c CmdBlob) fiWriteCmd(fiw *textproto.FIWriter) error { - ez := &ezfiw{fiw: fiw} - - ez.WriteLine("blob") - if c.Mark > 0 { - ez.WriteMark(c.Mark) - } - ez.WriteData(c.Data) - - return ez.err -} - -type CmdCheckpoint struct{} - -func (c CmdCheckpoint) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("checkpoint") -} - -type CmdProgress struct { - Str string -} - -func (c CmdProgress) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("progress", c.Str) -} - -type CmdGetMark struct { - Mark int -} - -func (c CmdGetMark) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("get-mark", ":"+strconv.Itoa(c.Mark)) -} - -type CmdCatBlob struct { - DataRef string -} - -func (c CmdCatBlob) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("cat-blob", c.DataRef) -} - -// See FileLs for using ls inside of a commit -type CmdLs struct { - DataRef string - Path textproto.Path -} - -func (c CmdLs) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("ls", c.DataRef, c.Path) -} - -type CmdFeature struct { - Feature string - Argument string -} - -func (c CmdFeature) fiWriteCmd(fiw *textproto.FIWriter) error { - if c.Argument != "" { - return fiw.WriteLine("feature", c.Feature+"="+c.Argument) - } else { - return fiw.WriteLine("feature", c.Feature) - } -} - -type CmdOption struct { - Option string -} - -func (c CmdOption) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("option", c.Option) -} - -type CmdDone struct{} - -func (c CmdDone) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("done") -} - -type CmdComment struct { - Comment string -} - -func (c CmdComment) fiWriteCmd(fiw *textproto.FIWriter) error { - return fiw.WriteLine("#" + c.Comment) -} diff --git a/fileactions.go b/fileactions.go deleted file mode 100644 index 910fe5a..0000000 --- a/fileactions.go +++ /dev/null @@ -1,94 +0,0 @@ -package libfastimport - -import ( - "git.lukeshu.com/go/libfastimport/textproto" -) - -type FileAction interface { - fiWriteFA(*textproto.FIWriter) error -} - -type FileModify struct { - Mode textproto.Mode - Path textproto.Path - DataRef string -} - -func (o FileModify) fiWriteFA(fiw *textproto.FIWriter) error { - return fiw.WriteLine("M", o.Mode, o.DataRef, o.Path) -} - -type FileModifyInline struct { - Mode textproto.Mode - Path textproto.Path - Data string -} - -func (o FileModifyInline) fiWriteFA(fiw *textproto.FIWriter) error { - ez := &ezfiw{fiw: fiw} - ez.WriteLine("M", o.Mode, "inline", o.Path) - ez.WriteData(o.Data) - return ez.err -} - -type FileDelete struct { - Path textproto.Path -} - -func (o FileDelete) fiWriteFA(fiw *textproto.FIWriter) error { - return fiw.WriteLine("D", o.Path) -} - -type FileCopy struct { - Src textproto.Path - Dst textproto.Path -} - -func (o FileCopy) fiWriteFA(fiw *textproto.FIWriter) error { - return fiw.WriteLine("C", o.Src, o.Dst) -} - -type FileRename struct { - Src string - Dst string -} - -func (o FileRename) fiWriteFA(fiw *textproto.FIWriter) error { - return fiw.WriteLine("R", o.Src, o.Dst) -} - -type FileDeleteAll struct{} - -func (o FileDeleteAll) fiWriteFA(fiw *textproto.FIWriter) error { - return fiw.WriteLine("deleteall") -} - -type NoteModify struct { - CommitIsh string - DataRef string -} - -func (o NoteModify) fiWriteFA(fiw *textproto.FIWriter) error { - return fiw.WriteLine("N", o.DataRef, o.CommitIsh) -} - -type NoteModifyInline struct { - CommitIsh string - Data string -} - -func (o NoteModifyInline) fiWriteFA(fiw *textproto.FIWriter) error { - ez := &ezfiw{fiw: fiw} - ez.WriteLine("N", "inline", o.CommitIsh) - ez.WriteData(o.Data) - return ez.err -} - -// See CmdLs for using ls outside of a commit -type FileLs struct { - Path textproto.Path -} - -func (o FileLs) fiWriteFA(fiw *textproto.FIWriter) error { - return fiw.WriteLine("ls", o.Path) -} diff --git a/frontend.go b/frontend.go index 2150a35..604e7af 100644 --- a/frontend.go +++ b/frontend.go @@ -17,16 +17,6 @@ func (e UnsupportedCommand) Error() string { return "Unsupported command: " + string(e) } -func trimLinePrefix(line string, prefix string) string { - if !strings.HasPrefix(line, prefix) { - panic("line didn't have prefix") - } - if !strings.HasSuffix(line, "\n") { - panic("line didn't have prefix") - } - return strings.TrimSuffix(strings.TrimPrefix(line, prefix), "\n") -} - // A Frontend is something that produces a fast-import stream; the // Frontend object provides methods for reading from it. type Frontend struct { @@ -34,6 +24,8 @@ type Frontend struct { cbw *textproto.CatBlobWriter w *bufio.Writer + inCommit bool + cmd chan Cmd err error } @@ -85,38 +77,6 @@ func (f *Frontend) nextLine() (line string, err error) { } } -func parse_data(line string) (data string, err error) { - nl := strings.IndexByte(line, '\n') - if nl < 0 { - return "", fmt.Errorf("data: expected newline: %v", data) - } - head := line[:nl+1] - rest := line[nl+1:] - if !strings.HasPrefix(head, "data ") { - return "", fmt.Errorf("data: could not parse: %v", data) - } - if strings.HasPrefix(head, "data <<") { - // Delimited format - delim := trimLinePrefix(head, "data <<") - suffix := "\n" + delim + "\n" - if !strings.HasSuffix(rest, suffix) { - return "", fmt.Errorf("data: did not find suffix: %v", suffix) - } - data = strings.TrimSuffix(rest, suffix) - } else { - // Exact byte count format - size, err := strconv.Atoi(trimLinePrefix(head, "data ")) - if err != nil { - return "", err - } - if size != len(rest) { - panic("FIReader should not have let this happen") - } - data = rest - } - return -} - func (f *Frontend) parse() error { line, err := f.nextLine() if err != nil { @@ -229,84 +189,71 @@ func (f *Frontend) parse() error { return err } } - for { - switch { - case strings.HasPrefix(line, "M "): - str := trimLinePrefix(line, "M ") - sp1 := strings.IndexByte(str, ' ') - sp2 := strings.IndexByte(str, ' ') - if sp1 < 0 || sp2 < 0 { - return fmt.Errorf("commit: malformed modify command: %v", line) - } - nMode, err := strconv.ParseUint(str[:sp1], 8, 18) - if err != nil { - return err - } - ref := str[sp1+1 : sp2] - path := textproto.PathUnescape(str[sp2+1:]) - if ref == "inline" { - line, err = f.nextLine() - if err != nil { - return err - } - if !strings.HasPrefix(line, "data ") { - return fmt.Errorf("commit: modify: expected data command: %v", line) - } - data, err := parse_data(line) - if err != nil { - return err - } - c.Tree = append(c.Tree, FileModifyInline{Mode: textproto.Mode(nMode), Path: path, Data: data}) - } else { - c.Tree = append(c.Tree, FileModify{Mode: textproto.Mode(nMode), Path: path, DataRef: ref}) - } - case strings.HasPrefix(line, "D "): - c.Tree = append(c.Tree, FileDelete{Path: textproto.PathUnescape(trimLinePrefix(line, "D "))}) - case strings.HasPrefix(line, "C "): - // BUG(lukeshu): TODO: commit C not implemented - panic("TODO: commit C not implemented") - case strings.HasPrefix(line, "R "): - // BUG(lukeshu): TODO: commit R not implemented - panic("TODO: commit R not implemented") - case strings.HasPrefix(line, "N "): - str := trimLinePrefix(line, "N ") - sp := strings.IndexByte(str, ' ') - if sp < 0 { - return fmt.Errorf("commit: malformed notemodify command: %v", line) - } - ref := str[:sp] - commitish := str[sp+1:] - if ref == "inline" { - line, err = f.nextLine() - if err != nil { - return err - } - if !strings.HasPrefix(line, "data ") { - return fmt.Errorf("commit: notemodify: expected data command: %v", line) - } - data, err := parse_data(line) - if err != nil { - return err - } - c.Tree = append(c.Tree, NoteModifyInline{CommitIsh: commitish, Data: data}) - } else { - c.Tree = append(c.Tree, NoteModify{CommitIsh: commitish, DataRef: ref}) - } - case strings.HasPrefix(line, "ls "): - // BUG(lukeshu): TODO: in-commit ls not implemented - panic("TODO: in-commit ls not implemented") - case line == "deleteall\n": - c.Tree = append(c.Tree, FileDeleteAll{}) - default: - break + f.cmd <- c + case strings.HasPrefix(line, "M "): + fmt.Printf("line: %q\n", line) + str := trimLinePrefix(line, "M ") + sp1 := strings.IndexByte(str, ' ') + sp2 := strings.IndexByte(str[sp1+1:], ' ') + if sp1 < 0 || sp2 < 0 { + return fmt.Errorf("commit: malformed modify command: %v", line) + } + nMode, err := strconv.ParseUint(str[:sp1], 8, 18) + if err != nil { + return err + } + ref := str[sp1+1 : sp2] + path := textproto.PathUnescape(str[sp2+1:]) + if ref == "inline" { + line, err = f.nextLine() + if err != nil { + return err + } + if !strings.HasPrefix(line, "data ") { + return fmt.Errorf("commit: modify: expected data command: %v", line) + } + data, err := parse_data(line) + if err != nil { + return err } + f.cmd <- FileModifyInline{Mode: textproto.Mode(nMode), Path: path, Data: data} + } else { + f.cmd <- FileModify{Mode: textproto.Mode(nMode), Path: path, DataRef: ref} + } + case strings.HasPrefix(line, "D "): + f.cmd <- FileDelete{Path: textproto.PathUnescape(trimLinePrefix(line, "D "))} + case strings.HasPrefix(line, "C "): + // BUG(lukeshu): TODO: commit C not implemented + panic("TODO: commit C not implemented") + case strings.HasPrefix(line, "R "): + // BUG(lukeshu): TODO: commit R not implemented + panic("TODO: commit R not implemented") + case strings.HasPrefix(line, "N "): + str := trimLinePrefix(line, "N ") + sp := strings.IndexByte(str, ' ') + if sp < 0 { + return fmt.Errorf("commit: malformed notemodify command: %v", line) + } + ref := str[:sp] + commitish := str[sp+1:] + if ref == "inline" { line, err = f.nextLine() if err != nil { return err } + if !strings.HasPrefix(line, "data ") { + return fmt.Errorf("commit: notemodify: expected data command: %v", line) + } + data, err := parse_data(line) + if err != nil { + return err + } + f.cmd <- NoteModifyInline{CommitIsh: commitish, Data: data} + } else { + f.cmd <- NoteModify{CommitIsh: commitish, DataRef: ref} } - f.cmd <- c - continue + case line == "deleteall\n": + f.cmd <- FileDeleteAll{} case strings.HasPrefix(line, "feature "): // 'feature' SP ('=' )? LF str := trimLinePrefix(line, "feature ") @@ -323,16 +270,17 @@ func (f *Frontend) parse() error { } case strings.HasPrefix(line, "ls "): // 'ls' SP SP LF - sp1 := strings.IndexByte(line, ' ') - sp2 := strings.IndexByte(line[sp1+1:], ' ') - lf := strings.IndexByte(line[sp2+1:], '\n') - if sp1 < 0 || sp2 < 0 || lf < 0 { - return fmt.Errorf("ls: outside of a commit both and are required: %v", line) + str := trimLinePrefix(line, "ls ") + sp := -1 + if !strings.HasPrefix(str, "\"") { + sp = strings.IndexByte(line, ' ') } - f.cmd <- CmdLs{ - DataRef: line[sp1+1 : sp2], - Path: textproto.PathUnescape(line[sp2+1 : lf]), + c := CmdLs{} + c.Path = textproto.PathUnescape(str[sp+1:]) + if sp >= 0 { + c.DataRef = str[:sp] } + f.cmd <- c case strings.HasPrefix(line, "option "): // 'option' SP