git/odb: add implementation of annotated Tag object

This commit is contained in:
Taylor Blau 2017-12-11 15:21:13 -08:00
parent 1e239fe699
commit b78fa93988
2 changed files with 183 additions and 0 deletions

118
git/odb/tag.go Normal file

@ -0,0 +1,118 @@
package odb
import (
"bufio"
"bytes"
"encoding/hex"
"fmt"
"io"
"strings"
"github.com/git-lfs/git-lfs/errors"
)
type Tag struct {
Object []byte
ObjectType ObjectType
Name string
Tagger string
Message string
}
// Decode implements Object.Decode and decodes the uncompressed tag being
// read. It returns the number of uncompressed bytes being consumed off of the
// stream, which should be strictly equal to the size given.
//
// If any error was encountered along the way it will be returned, and the
// receiving *Tag is considered invalid.
func (t *Tag) Decode(r io.Reader, size int64) (int, error) {
scanner := bufio.NewScanner(io.LimitReader(r, size))
var (
finishedHeaders bool
message []string
)
for scanner.Scan() {
if finishedHeaders {
message = append(message, scanner.Text())
} else {
if len(scanner.Bytes()) == 0 {
finishedHeaders = true
continue
}
parts := strings.SplitN(scanner.Text(), " ", 2)
if len(parts) < 2 {
return 0, errors.Errorf("git/odb: invalid tag header: %s", scanner.Text())
}
switch parts[0] {
case "object":
sha, err := hex.DecodeString(parts[1])
if err != nil {
return 0, errors.Wrap(err, "git/odb: unable to decode SHA-1")
}
t.Object = sha
case "type":
t.ObjectType = ObjectTypeFromString(parts[1])
case "tag":
t.Name = parts[1]
case "tagger":
t.Tagger = parts[1]
default:
return 0, errors.Errorf("git/odb: unknown tag header: %s", parts[0])
}
}
}
if err := scanner.Err(); err != nil {
return 0, err
}
t.Message = strings.Join(message, "\n")
return int(size), nil
}
// Encode encodes the Tag's contents to the given io.Writer, "w". If there was
// any error copying the Tag's contents, that error will be returned.
//
// Otherwise, the number of bytes written will be returned.
func (t *Tag) Encode(w io.Writer) (int, error) {
headers := []string{
fmt.Sprintf("object %s", hex.EncodeToString(t.Object)),
fmt.Sprintf("type %s", t.ObjectType),
fmt.Sprintf("tag %s", t.Name),
fmt.Sprintf("tagger %s", t.Tagger),
}
return fmt.Fprintf(w, "%s\n\n%s\n", strings.Join(headers, "\n"), t.Message)
}
// Equal returns whether the receiving and given Tags are equal, or in other
// words, whether they are represented by the same SHA-1 when saved to the
// object database.
func (t *Tag) Equal(other *Tag) bool {
if (t == nil) != (other == nil) {
return false
}
if t != nil {
return bytes.Equal(t.Object, other.Object) &&
t.ObjectType == other.ObjectType &&
t.Name == other.Name &&
t.Tagger == other.Tagger &&
t.Message == other.Message
}
return true
}
// Type implements Object.ObjectType by returning the correct object type for
// Tags, TagObjectType.
func (t *Tag) Type() ObjectType {
return TagObjectType
}

65
git/odb/tag_test.go Normal file

@ -0,0 +1,65 @@
package odb
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestTagTypeReturnsCorrectObjectType(t *testing.T) {
assert.Equal(t, TagObjectType, new(Tag).Type())
}
func TestTagEncode(t *testing.T) {
tag := &Tag{
Object: []byte("aaaaaaaaaaaaaaaaaaaa"),
ObjectType: CommitObjectType,
Name: "v2.4.0",
Tagger: "A U Thor <author@example.com>",
Message: "The quick brown fox jumps over the lazy dog.",
}
buf := new(bytes.Buffer)
n, err := tag.Encode(buf)
assert.Nil(t, err)
assert.EqualValues(t, buf.Len(), n)
assertLine(t, buf, "object 6161616161616161616161616161616161616161")
assertLine(t, buf, "type commit")
assertLine(t, buf, "tag v2.4.0")
assertLine(t, buf, "tagger A U Thor <author@example.com>")
assertLine(t, buf, "")
assertLine(t, buf, "The quick brown fox jumps over the lazy dog.")
assert.Equal(t, 0, buf.Len())
}
func TestTagDecode(t *testing.T) {
from := new(bytes.Buffer)
fmt.Fprintf(from, "object 6161616161616161616161616161616161616161\n")
fmt.Fprintf(from, "type commit\n")
fmt.Fprintf(from, "tag v2.4.0\n")
fmt.Fprintf(from, "tagger A U Thor <author@example.com>\n")
fmt.Fprintf(from, "\n")
fmt.Fprintf(from, "The quick brown fox jumps over the lazy dog.\n")
flen := from.Len()
tag := new(Tag)
n, err := tag.Decode(from, int64(flen))
assert.Nil(t, err)
assert.Equal(t, n, flen)
assert.Equal(t, []byte("aaaaaaaaaaaaaaaaaaaa"), tag.Object)
assert.Equal(t, CommitObjectType, tag.ObjectType)
assert.Equal(t, "v2.4.0", tag.Name)
assert.Equal(t, "A U Thor <author@example.com>", tag.Tagger)
assert.Equal(t, "The quick brown fox jumps over the lazy dog.", tag.Message)
}