git/odb: add implementation of annotated Tag object
This commit is contained in:
parent
1e239fe699
commit
b78fa93988
118
git/odb/tag.go
Normal file
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
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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user