1// Copyright 2015 The Gogs Authors. All rights reserved.
2// Use of this source code is governed by a MIT-style
3// license that can be found in the LICENSE file.
4
5package git
6
7import (
8	"bytes"
9	"sort"
10	"strings"
11)
12
13const beginpgp = "\n-----BEGIN PGP SIGNATURE-----\n"
14const endpgp = "\n-----END PGP SIGNATURE-----"
15
16// Tag represents a Git tag.
17type Tag struct {
18	Name      string
19	ID        SHA1
20	Object    SHA1 // The id of this commit object
21	Type      string
22	Tagger    *Signature
23	Message   string
24	Signature *CommitGPGSignature
25}
26
27// Commit return the commit of the tag reference
28func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) {
29	return gitRepo.getCommit(tag.Object)
30}
31
32// Parse commit information from the (uncompressed) raw
33// data from the commit object.
34// \n\n separate headers from message
35func parseTagData(data []byte) (*Tag, error) {
36	tag := new(Tag)
37	tag.Tagger = &Signature{}
38	// we now have the contents of the commit object. Let's investigate...
39	nextline := 0
40l:
41	for {
42		eol := bytes.IndexByte(data[nextline:], '\n')
43		switch {
44		case eol > 0:
45			line := data[nextline : nextline+eol]
46			spacepos := bytes.IndexByte(line, ' ')
47			reftype := line[:spacepos]
48			switch string(reftype) {
49			case "object":
50				id, err := NewIDFromString(string(line[spacepos+1:]))
51				if err != nil {
52					return nil, err
53				}
54				tag.Object = id
55			case "type":
56				// A commit can have one or more parents
57				tag.Type = string(line[spacepos+1:])
58			case "tagger":
59				sig, err := newSignatureFromCommitline(line[spacepos+1:])
60				if err != nil {
61					return nil, err
62				}
63				tag.Tagger = sig
64			}
65			nextline += eol + 1
66		case eol == 0:
67			tag.Message = string(data[nextline+1:])
68			break l
69		default:
70			break l
71		}
72	}
73	idx := strings.LastIndex(tag.Message, beginpgp)
74	if idx > 0 {
75		endSigIdx := strings.Index(tag.Message[idx:], endpgp)
76		if endSigIdx > 0 {
77			tag.Signature = &CommitGPGSignature{
78				Signature: tag.Message[idx+1 : idx+endSigIdx+len(endpgp)],
79				Payload:   string(data[:bytes.LastIndex(data, []byte(beginpgp))+1]),
80			}
81			tag.Message = tag.Message[:idx+1]
82		}
83	}
84	return tag, nil
85}
86
87type tagSorter []*Tag
88
89func (ts tagSorter) Len() int {
90	return len([]*Tag(ts))
91}
92
93func (ts tagSorter) Less(i, j int) bool {
94	return []*Tag(ts)[i].Tagger.When.After([]*Tag(ts)[j].Tagger.When)
95}
96
97func (ts tagSorter) Swap(i, j int) {
98	[]*Tag(ts)[i], []*Tag(ts)[j] = []*Tag(ts)[j], []*Tag(ts)[i]
99}
100
101// sortTagsByTime
102func sortTagsByTime(tags []*Tag) {
103	sorter := tagSorter(tags)
104	sort.Sort(sorter)
105}
106