Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions plumbing/protocol/packp/advrefs_decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,6 @@ func isPrefix(payload []byte) bool {
return len(payload) > 0 && payload[0] == '#'
}

func isFlush(payload []byte) bool {
return len(payload) == 0
}

// If the first hash is zero, then a no-refs is comming. Otherwise, a
// list-of-refs is comming, and the hash will be followed by the first
// advertised ref.
Expand Down
4 changes: 4 additions & 0 deletions plumbing/protocol/packp/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ var (
// updreq
shallowNoSp = []byte("shallow")
)

func isFlush(payload []byte) bool {
return len(payload) == 0
}
145 changes: 145 additions & 0 deletions plumbing/protocol/packp/report_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package packp

import (
"io"

"fmt"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
"strings"
)

const (
ok = "ok"
)

// ReportStatus is a report status message, as used in the git-receive-pack
// process whenever the 'report-status' capability is negotiated.
type ReportStatus struct {
UnpackStatus string
CommandStatuses []*CommandStatus
}

// NewReportStatus creates a new ReportStatus message.
func NewReportStatus() *ReportStatus {
return &ReportStatus{}
}

// Ok returns true if the report status reported no error.
func (s *ReportStatus) Ok() bool {
return s.UnpackStatus == ok
}

// Encode writes the report status to a writer.
func (s *ReportStatus) Encode(w io.Writer) error {
e := pktline.NewEncoder(w)
if err := e.Encodef("unpack %s", s.UnpackStatus); err != nil {
return err
}

for _, cs := range s.CommandStatuses {
if err := cs.encode(w); err != nil {
return err
}
}

return e.Flush()
}

// Decode reads from the given reader and decodes a report-status message. It
// does not read more input than what is needed to fill the report status.
func (s *ReportStatus) Decode(r io.Reader) error {
scan := pktline.NewScanner(r)
if err := s.scanFirstLine(scan); err != nil {
return err
}

if err := s.decodeReportStatus(scan.Bytes()); err != nil {
return err
}

flushed := false
for scan.Scan() {
b := scan.Bytes()
if isFlush(b) {
flushed = true
break
}

if err := s.decodeCommandStatus(b); err != nil {
return err
}
}

if !flushed {
return fmt.Errorf("missing flush")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can you reach this point without flushed being true? (with this code)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when scan.Scan() = false in the first iteration.

}

return scan.Err()
}

func (s *ReportStatus) scanFirstLine(scan *pktline.Scanner) error {
if scan.Scan() {
return nil
}

if scan.Err() != nil {
return scan.Err()
}

return io.ErrUnexpectedEOF
}

func (s *ReportStatus) decodeReportStatus(b []byte) error {
if isFlush(b) {
return fmt.Errorf("premature flush")
}

line := string(b)
fields := strings.SplitN(line, " ", 2)
if len(fields) != 2 || fields[0] != "unpack" {
return fmt.Errorf("malformed unpack status: %s", line)
}

s.UnpackStatus = fields[1]
return nil
}

func (s *ReportStatus) decodeCommandStatus(b []byte) error {
line := string(b)
fields := strings.SplitN(line, " ", 3)
status := ok
if len(fields) == 3 && fields[0] == "ng" {
status = fields[2]
} else if len(fields) != 2 || fields[0] != "ok" {
return fmt.Errorf("malformed command status: %s", line)
}

cs := &CommandStatus{
ReferenceName: plumbing.ReferenceName(fields[1]),
Status: status,
}
s.CommandStatuses = append(s.CommandStatuses, cs)
return nil
}

// CommandStatus is the status of a reference in a report status.
// See ReportStatus struct.
type CommandStatus struct {
ReferenceName plumbing.ReferenceName
Status string
}

// Ok returns true if the command status reported no error.
func (s *CommandStatus) Ok() bool {
return s.Status == ok
}

func (s *CommandStatus) encode(w io.Writer) error {
e := pktline.NewEncoder(w)
if s.Ok() {
return e.Encodef("ok %s", s.ReferenceName.String())
}

return e.Encodef("ng %s %s", s.ReferenceName.String(), s.Status)
}
Loading