summaryrefslogtreecommitdiffstats
path: root/modules/git/object_id.go
blob: ecbc1587d4468e90a29c01ad6df0769312bb6a75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package git

import (
	"bytes"
	"encoding/hex"
	"fmt"
)

type ObjectID interface {
	String() string
	IsZero() bool
	RawValue() []byte
	Type() ObjectFormat
}

/* SHA1 */
type Sha1Hash [20]byte

func (h *Sha1Hash) String() string {
	return hex.EncodeToString(h[:])
}

func (h *Sha1Hash) IsZero() bool {
	empty := Sha1Hash{}
	return bytes.Equal(empty[:], h[:])
}
func (h *Sha1Hash) RawValue() []byte { return h[:] }
func (*Sha1Hash) Type() ObjectFormat { return Sha1ObjectFormat }

var _ ObjectID = &Sha1Hash{}

func MustIDFromString(hexHash string) ObjectID {
	id, err := NewIDFromString(hexHash)
	if err != nil {
		panic(err)
	}
	return id
}

/* SHA256 */
type Sha256Hash [32]byte

func (h *Sha256Hash) String() string {
	return hex.EncodeToString(h[:])
}

func (h *Sha256Hash) IsZero() bool {
	empty := Sha256Hash{}
	return bytes.Equal(empty[:], h[:])
}
func (h *Sha256Hash) RawValue() []byte { return h[:] }
func (*Sha256Hash) Type() ObjectFormat { return Sha256ObjectFormat }

/* utility */
func NewIDFromString(hexHash string) (ObjectID, error) {
	var theObjectFormat ObjectFormat
	for _, objectFormat := range SupportedObjectFormats {
		if len(hexHash) == objectFormat.FullLength() {
			theObjectFormat = objectFormat
			break
		}
	}

	if theObjectFormat == nil {
		return nil, fmt.Errorf("length %d has no matched object format: %s", len(hexHash), hexHash)
	}

	b, err := hex.DecodeString(hexHash)
	if err != nil {
		return nil, err
	}

	if len(b) != theObjectFormat.FullLength()/2 {
		return theObjectFormat.EmptyObjectID(), fmt.Errorf("length must be %d: %v", theObjectFormat.FullLength(), b)
	}
	return theObjectFormat.MustID(b), nil
}

// IsEmptyCommitID checks if an hexadecimal string represents an empty commit according to git (only '0').
// If objectFormat is not nil, the length will be checked as well (otherwise the length must match the sha1 or sha256 length).
func IsEmptyCommitID(commitID string, objectFormat ObjectFormat) bool {
	if commitID == "" {
		return true
	}
	if objectFormat == nil {
		if Sha1ObjectFormat.FullLength() != len(commitID) && Sha256ObjectFormat.FullLength() != len(commitID) {
			return false
		}
	} else if objectFormat.FullLength() != len(commitID) {
		return false
	}
	for _, c := range commitID {
		if c != '0' {
			return false
		}
	}
	return true
}

// ComputeBlobHash compute the hash for a given blob content
func ComputeBlobHash(hashType ObjectFormat, content []byte) ObjectID {
	return hashType.ComputeHash(ObjectBlob, content)
}

type ErrInvalidSHA struct {
	SHA string
}

func (err ErrInvalidSHA) Error() string {
	return fmt.Sprintf("invalid sha: %s", err.SHA)
}