19 changes: 12 additions & 7 deletions modules/git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import (
// Commit represents a git commit.
type Commit struct {
Tree
ID SHA1 // The ID of this commit object
ID ObjectID // The ID of this commit object
Author *Signature
Committer *Signature
CommitMessage string
Signature *CommitGPGSignature

Parents []SHA1 // SHA1 strings
Parents []ObjectID // ID strings
submoduleCache *ObjectCache
}

Expand All @@ -50,9 +50,9 @@ func (c *Commit) Summary() string {

// ParentID returns oid of n-th parent (0-based index).
// It returns nil if no such parent exists.
func (c *Commit) ParentID(n int) (SHA1, error) {
func (c *Commit) ParentID(n int) (ObjectID, error) {
if n >= len(c.Parents) {
return SHA1{}, ErrNotExist{"", ""}
return nil, ErrNotExist{"", ""}
}
return c.Parents[n], nil
}
Expand Down Expand Up @@ -209,9 +209,9 @@ func (c *Commit) CommitsBefore() ([]*Commit, error) {
}

// HasPreviousCommit returns true if a given commitHash is contained in commit's parents
func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
func (c *Commit) HasPreviousCommit(objectID ObjectID) (bool, error) {
this := c.ID.String()
that := commitHash.String()
that := objectID.String()

if this == that {
return false, nil
Expand All @@ -232,9 +232,14 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {

// IsForcePush returns true if a push from oldCommitHash to this is a force push
func (c *Commit) IsForcePush(oldCommitID string) (bool, error) {
if oldCommitID == EmptySHA {
objectFormat, err := c.repo.GetObjectFormat()
if err != nil {
return false, err
}
if oldCommitID == objectFormat.Empty().String() {
return false, nil
}

oldCommit, err := c.repo.GetCommit(oldCommitID)
if err != nil {
return false, err
Expand Down
4 changes: 2 additions & 2 deletions modules/git/commit_convert_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ func convertPGPSignature(c *object.Commit) *CommitGPGSignature {

func convertCommit(c *object.Commit) *Commit {
return &Commit{
ID: c.Hash,
ID: ParseGogitHash(c.Hash),
CommitMessage: c.Message,
Committer: &c.Committer,
Author: &c.Author,
Signature: convertPGPSignature(c),
Parents: c.ParentHashes,
Parents: ParseGogitHashArray(c.ParentHashes),
}
}
2 changes: 1 addition & 1 deletion modules/git/commit_info_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
defer commitGraphFile.Close()
}

c, err := commitNodeIndex.Get(commit.ID)
c, err := commitNodeIndex.Get(plumbing.Hash(commit.ID.RawValue()))
if err != nil {
return nil, nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion modules/git/commit_info_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string,
if typ != "commit" {
return nil, fmt.Errorf("unexpected type: %s for commit id: %s", typ, commitID)
}
c, err = CommitFromReader(commit.repo, MustIDFromString(commitID), io.LimitReader(batchReader, size))
c, err = CommitFromReader(commit.repo, commit.ID.Type().MustIDFromString(commitID), io.LimitReader(batchReader, size))
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions modules/git/commit_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (
// We need this to interpret commits from cat-file or cat-file --batch
//
// If used as part of a cat-file --batch stream you need to limit the reader to the correct size
func CommitFromReader(gitRepo *Repository, sha SHA1, reader io.Reader) (*Commit, error) {
func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader) (*Commit, error) {
commit := &Commit{
ID: sha,
ID: objectID,
Author: &Signature{},
Committer: &Signature{},
}
Expand Down Expand Up @@ -71,10 +71,10 @@ readLoop:

switch string(split[0]) {
case "tree":
commit.Tree = *NewTree(gitRepo, MustIDFromString(string(data)))
commit.Tree = *NewTree(gitRepo, objectID.Type().MustIDFromString(string(data)))
_, _ = payloadSB.Write(line)
case "parent":
commit.Parents = append(commit.Parents, MustIDFromString(string(data)))
commit.Parents = append(commit.Parents, objectID.Type().MustIDFromString(string(data)))
_, _ = payloadSB.Write(line)
case "author":
commit.Author = &Signature{}
Expand Down
6 changes: 3 additions & 3 deletions modules/git/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ gpgsig -----BEGIN PGP SIGNATURE-----
empty commit`

sha := SHA1{0xfe, 0xaf, 0x4b, 0xa6, 0xbc, 0x63, 0x5f, 0xec, 0x44, 0x2f, 0x46, 0xdd, 0xd4, 0x51, 0x24, 0x16, 0xec, 0x43, 0xc2, 0xc2}
sha := &Sha1Hash{0xfe, 0xaf, 0x4b, 0xa6, 0xbc, 0x63, 0x5f, 0xec, 0x44, 0x2f, 0x46, 0xdd, 0xd4, 0x51, 0x24, 0x16, 0xec, 0x43, 0xc2, 0xc2}
gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare"))
assert.NoError(t, err)
assert.NotNil(t, gitRepo)
Expand Down Expand Up @@ -135,8 +135,8 @@ func TestHasPreviousCommit(t *testing.T) {
commit, err := repo.GetCommit("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0")
assert.NoError(t, err)

parentSHA := MustIDFromString("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2")
notParentSHA := MustIDFromString("2839944139e0de9737a044f78b0e4b40d989a9e3")
parentSHA := repo.objectFormat.MustIDFromString("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2")
notParentSHA := repo.objectFormat.MustIDFromString("2839944139e0de9737a044f78b0e4b40d989a9e3")

haz, err := commit.HasPreviousCommit(parentSHA)
assert.NoError(t, err)
Expand Down
10 changes: 7 additions & 3 deletions modules/git/last_commit_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,21 @@ func (c *LastCommitCache) Get(ref, entryPath string) (*Commit, error) {

// GetCommitByPath gets the last commit for the entry in the provided commit
func (c *LastCommitCache) GetCommitByPath(commitID, entryPath string) (*Commit, error) {
sha1, err := NewIDFromString(commitID)
objectFormat, err := c.repo.GetObjectFormat()
if err != nil {
return nil, err
}
sha, err := objectFormat.NewIDFromString(commitID)
if err != nil {
return nil, err
}

lastCommit, err := c.Get(sha1.String(), entryPath)
lastCommit, err := c.Get(sha.String(), entryPath)
if err != nil || lastCommit != nil {
return lastCommit, err
}

lastCommit, err = c.repo.getCommitByPathWithID(sha1, entryPath)
lastCommit, err = c.repo.getCommitByPathWithID(sha, entryPath)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion modules/git/last_commit_cache_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package git
import (
"context"

"github.com/go-git/go-git/v5/plumbing"
cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
)

Expand All @@ -18,7 +19,7 @@ func (c *Commit) CacheCommit(ctx context.Context) error {
}
commitNodeIndex, _ := c.repo.CommitNodeIndex()

index, err := commitNodeIndex.Get(c.ID)
index, err := commitNodeIndex.Get(plumbing.Hash(c.ID.RawValue()))
if err != nil {
return err
}
Expand Down
13 changes: 8 additions & 5 deletions modules/git/log_name_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,20 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int
}

// Our "line" must look like: <commitid> SP (<parent> SP) * NUL
ret.CommitID = string(g.next[0:40])
parents := string(g.next[41:])
commitIds := string(g.next)
if g.buffull {
more, err := g.rd.ReadString('\x00')
if err != nil {
return nil, err
}
parents += more
commitIds += more
}
commitIds = commitIds[:len(commitIds)-1]
splitIds := strings.Split(commitIds, " ")
ret.CommitID = splitIds[0]
if len(splitIds) > 1 {
ret.ParentIDs = splitIds[1:]
}
parents = parents[:len(parents)-1]
ret.ParentIDs = strings.Split(parents, " ")

// now read the next "line"
g.buffull = false
Expand Down
3 changes: 2 additions & 1 deletion modules/git/notes_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"code.gitea.io/gitea/modules/log"

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
)

Expand Down Expand Up @@ -72,7 +73,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note)
defer commitGraphFile.Close()
}

commitNode, err := commitNodeIndex.Get(notes.ID)
commitNode, err := commitNodeIndex.Get(plumbing.Hash(notes.ID.RawValue()))
if err != nil {
return err
}
Expand Down
103 changes: 103 additions & 0 deletions modules/git/object_format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package git

import (
"crypto/sha1"
"fmt"
"regexp"
"strings"
)

type ObjectFormatID int

const (
Sha1 ObjectFormatID = iota
Copy link
Contributor

@wxiaoguang wxiaoguang Dec 14, 2023

Choose a reason for hiding this comment

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

Why it needs "integer" number?

I think type ObjectFormat string and Sha1 = "sha1" work better?

Copy link
Member

Choose a reason for hiding this comment

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

I think so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is another function that does this from string already, at the bottom of the file.

git.ObjectFormatFromString()

Basically, there is no need for this to be int or string in particular, but I wanted to move at least temporarily away from string to catch errors like passing "SHA1" vs. "sha1".

Finally, maybe my prejudice shows, coming from C and C++ world shows, where enums are ints 😄

Copy link
Member

Choose a reason for hiding this comment

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

)

// sha1Pattern can be used to determine if a string is an valid sha
var sha1Pattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`)

type ObjectFormat interface {
ID() ObjectFormatID
String() string

// Empty is the hash of empty git
Empty() ObjectID
// EmptyTree is the hash of an empty tree
EmptyTree() ObjectID
// FullLength is the length of the hash's hex string
FullLength() int

IsValid(input string) bool
MustID(b []byte) ObjectID
MustIDFromString(s string) ObjectID
NewID(b []byte) (ObjectID, error)
NewIDFromString(s string) (ObjectID, error)
NewEmptyID() ObjectID

NewHasher() HasherInterface
}

/* SHA1 Type */
type Sha1ObjectFormat struct{}

func (*Sha1ObjectFormat) ID() ObjectFormatID { return Sha1 }
func (*Sha1ObjectFormat) String() string { return "sha1" }
func (*Sha1ObjectFormat) Empty() ObjectID { return &Sha1Hash{} }
func (*Sha1ObjectFormat) EmptyTree() ObjectID {
return &Sha1Hash{
0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04,
}
}
func (*Sha1ObjectFormat) FullLength() int { return 40 }
func (*Sha1ObjectFormat) IsValid(input string) bool {
return sha1Pattern.MatchString(input)
}

func (*Sha1ObjectFormat) MustID(b []byte) ObjectID {
var id Sha1Hash
copy(id[0:20], b)
return &id
}

func (h *Sha1ObjectFormat) MustIDFromString(s string) ObjectID {
return MustIDFromString(h, s)
}

func (h *Sha1ObjectFormat) NewID(b []byte) (ObjectID, error) {
return IDFromRaw(h, b)
}

func (h *Sha1ObjectFormat) NewIDFromString(s string) (ObjectID, error) {
return genericIDFromString(h, s)
}

func (*Sha1ObjectFormat) NewEmptyID() ObjectID {
return NewSha1()
}

func (h *Sha1ObjectFormat) NewHasher() HasherInterface {
return &Sha1Hasher{sha1.New()}
}

// utils
func ObjectFormatFromID(id ObjectFormatID) ObjectFormat {
switch id {
case Sha1:
return &Sha1ObjectFormat{}
}

return nil
}

func ObjectFormatFromString(hash string) (ObjectFormat, error) {
switch strings.ToLower(hash) {
case "sha1":
return &Sha1ObjectFormat{}, nil
}

return nil, fmt.Errorf("unknown hash type: %s", hash)
}
143 changes: 143 additions & 0 deletions modules/git/object_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package git

import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"hash"
"strconv"
"strings"
)

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{} }

func NewSha1() *Sha1Hash {
return &Sha1Hash{}
}

// generic implementations
func NewHash(hash string) (ObjectID, error) {
hash = strings.ToLower(hash)
switch hash {
case "sha1":
return &Sha1Hash{}, nil
}

return nil, errors.New("unsupported hash type")
}

func IDFromRaw(h ObjectFormat, b []byte) (ObjectID, error) {
if len(b) != h.FullLength()/2 {
return h.Empty(), fmt.Errorf("length must be %d: %v", h.FullLength(), b)
}
return h.MustID(b), nil
}

func MustIDFromString(h ObjectFormat, s string) ObjectID {
b, _ := hex.DecodeString(s)
return h.MustID(b)
}

func genericIDFromString(h ObjectFormat, s string) (ObjectID, error) {
s = strings.TrimSpace(s)
if len(s) != h.FullLength() {
return h.Empty(), fmt.Errorf("length must be %d: %s", h.FullLength(), s)
}
b, err := hex.DecodeString(s)
if err != nil {
return h.Empty(), err
}
return h.NewID(b)
}

// utils
func IDFromString(hexHash string) (ObjectID, error) {
switch len(hexHash) {
case 40:
hashType := Sha1ObjectFormat{}
h, err := hashType.NewIDFromString(hexHash)
if err != nil {
return nil, err
}
return h, nil
}

return nil, fmt.Errorf("invalid hash hex string: '%s' len: %d", hexHash, len(hexHash))
}

func IsEmptyCommitID(commitID string) bool {
if commitID == "" {
return true
}

id, err := IDFromString(commitID)
if err != nil {
return false
}

return id.IsZero()
}

// HashInterface is a struct that will generate a Hash
type HasherInterface interface {
hash.Hash

HashSum() ObjectID
}

type Sha1Hasher struct {
hash.Hash
}

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

// ComputeHash compute the hash for a given ObjectType and content
func ComputeHash(hashType ObjectFormat, t ObjectType, content []byte) ObjectID {
h := hashType.NewHasher()
_, _ = h.Write(t.Bytes())
_, _ = h.Write([]byte(" "))
_, _ = h.Write([]byte(strconv.FormatInt(int64(len(content)), 10)))
_, _ = h.Write([]byte{0})
return h.HashSum()
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and below, a regression ..... the "content" is not written into the hasher 😭

-> Fix incorrect object id hash function #30708

}

// Sum generates a SHA1 for the provided hash
func (h *Sha1Hasher) HashSum() ObjectID {
var sha1 Sha1Hash
copy(sha1[:], h.Hash.Sum(nil))
return &sha1
}

type ErrInvalidSHA struct {
SHA string
}

func (err ErrInvalidSHA) Error() string {
return fmt.Sprintf("invalid sha: %s", err.SHA)
}
28 changes: 28 additions & 0 deletions modules/git/object_id_gogit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build gogit

package git

import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/hash"
)

func ParseGogitHash(h plumbing.Hash) ObjectID {
switch hash.Size {
case 20:
return ObjectFormatFromID(Sha1).MustID(h[:])
}

return nil
}

func ParseGogitHashArray(objectIDs []plumbing.Hash) []ObjectID {
ret := make([]ObjectID, len(objectIDs))
for i, h := range objectIDs {
ret[i] = ParseGogitHash(h)
}

return ret
}
21 changes: 21 additions & 0 deletions modules/git/object_id_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package git

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestIsValidSHAPattern(t *testing.T) {
h := NewSha1().Type()
assert.True(t, h.IsValid("fee1"))
assert.True(t, h.IsValid("abc000"))
assert.True(t, h.IsValid("9023902390239023902390239023902390239023"))
assert.False(t, h.IsValid("90239023902390239023902390239023902390239023"))
assert.False(t, h.IsValid("abc"))
assert.False(t, h.IsValid("123g"))
assert.False(t, h.IsValid("some random text"))
}
16 changes: 10 additions & 6 deletions modules/git/parse_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (
"strconv"
"strings"

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/plumbing/object"
)

// ParseTreeEntries parses the output of a `git ls-tree -l` command.
func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
func ParseTreeEntries(h ObjectFormat, data []byte) ([]*TreeEntry, error) {
return parseTreeEntries(data, nil)
}

Expand Down Expand Up @@ -50,15 +52,16 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+6]))
}

if pos+40 > len(data) {
// in hex format, not byte format ....
if pos+hash.Size*2 > len(data) {
return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
}
id, err := NewIDFromString(string(data[pos : pos+40]))
var err error
entry.ID, err = IDFromString(string(data[pos : pos+hash.Size*2]))
if err != nil {
return nil, fmt.Errorf("Invalid ls-tree output: %w", err)
return nil, fmt.Errorf("invalid ls-tree output: %w", err)
}
entry.ID = id
entry.gogitTreeEntry.Hash = id
entry.gogitTreeEntry.Hash = plumbing.Hash(entry.ID.RawValue())
pos += 41 // skip over sha and trailing space

end := pos + bytes.IndexByte(data[pos:], '\t')
Expand All @@ -77,6 +80,7 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {

// In case entry name is surrounded by double quotes(it happens only in git-shell).
if data[pos] == '"' {
var err error
entry.gogitTreeEntry.Name, err = strconv.Unquote(string(data[pos:end]))
if err != nil {
return nil, fmt.Errorf("Invalid ls-tree output: %w", err)
Expand Down
20 changes: 13 additions & 7 deletions modules/git/parse_gogit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
package git

import (
"fmt"
"testing"

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/stretchr/testify/assert"
Expand All @@ -26,9 +28,9 @@ func TestParseTreeEntries(t *testing.T) {
Input: "100644 blob 61ab7345a1a3bbc590068ccae37b8515cfc5843c 1022\texample/file2.txt\n",
Expected: []*TreeEntry{
{
ID: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
ID: ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
gogitTreeEntry: &object.TreeEntry{
Hash: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()),
Name: "example/file2.txt",
Mode: filemode.Regular,
},
Expand All @@ -42,20 +44,20 @@ func TestParseTreeEntries(t *testing.T) {
"040000 tree 1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8 -\texample\n",
Expected: []*TreeEntry{
{
ID: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
ID: ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
gogitTreeEntry: &object.TreeEntry{
Hash: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()),
Name: "example/\n.txt",
Mode: filemode.Symlink,
},
size: 234131,
sized: true,
},
{
ID: MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"),
ID: ObjectFormatFromID(Sha1).MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"),
sized: true,
gogitTreeEntry: &object.TreeEntry{
Hash: MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"),
Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8").RawValue()),
Name: "example",
Mode: filemode.Dir,
},
Expand All @@ -65,8 +67,12 @@ func TestParseTreeEntries(t *testing.T) {
}

for _, testCase := range testCases {
entries, err := ParseTreeEntries([]byte(testCase.Input))
entries, err := ParseTreeEntries(ObjectFormatFromID(Sha1), []byte(testCase.Input))
assert.NoError(t, err)
if len(entries) > 1 {
fmt.Println(testCase.Expected[0].ID)
fmt.Println(entries[0].ID)
}
assert.EqualValues(t, testCase.Expected, entries)
}
}
16 changes: 8 additions & 8 deletions modules/git/parse_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import (
)

// ParseTreeEntries parses the output of a `git ls-tree -l` command.
func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
return parseTreeEntries(data, nil)
func ParseTreeEntries(objectFormat ObjectFormat, data []byte) ([]*TreeEntry, error) {
return parseTreeEntries(objectFormat, data, nil)
}

var sepSpace = []byte{' '}

func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
func parseTreeEntries(objectFormat ObjectFormat, data []byte, ptree *Tree) ([]*TreeEntry, error) {
var err error
entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1)
for pos := 0; pos < len(data); {
Expand Down Expand Up @@ -72,7 +72,7 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
return nil, fmt.Errorf("unknown type: %v", string(entryMode))
}

entry.ID, err = NewIDFromString(string(entryObjectID))
entry.ID, err = objectFormat.NewIDFromString(string(entryObjectID))
if err != nil {
return nil, fmt.Errorf("invalid ls-tree output (invalid object id): %q, err: %w", line, err)
}
Expand All @@ -92,15 +92,15 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
return entries, nil
}

func catBatchParseTreeEntries(ptree *Tree, rd *bufio.Reader, sz int64) ([]*TreeEntry, error) {
func catBatchParseTreeEntries(objectFormat ObjectFormat, ptree *Tree, rd *bufio.Reader, sz int64) ([]*TreeEntry, error) {
fnameBuf := make([]byte, 4096)
modeBuf := make([]byte, 40)
shaBuf := make([]byte, 40)
shaBuf := make([]byte, objectFormat.FullLength())
entries := make([]*TreeEntry, 0, 10)

loop:
for sz > 0 {
mode, fname, sha, count, err := ParseTreeLine(rd, modeBuf, fnameBuf, shaBuf)
mode, fname, sha, count, err := ParseTreeLine(objectFormat, rd, modeBuf, fnameBuf, shaBuf)
if err != nil {
if err == io.EOF {
break loop
Expand All @@ -127,7 +127,7 @@ loop:
return nil, fmt.Errorf("unknown mode: %v", string(mode))
}

entry.ID = MustID(sha)
entry.ID = objectFormat.MustID(sha)
entry.name = string(fname)
entries = append(entries, entry)
}
Expand Down
22 changes: 13 additions & 9 deletions modules/git/parse_nogogit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
)

func TestParseTreeEntriesLong(t *testing.T) {
objectFormat := ObjectFormatFromID(Sha1)

testCases := []struct {
Input string
Expected []*TreeEntry
Expand All @@ -24,28 +26,28 @@ func TestParseTreeEntriesLong(t *testing.T) {
`,
Expected: []*TreeEntry{
{
ID: MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
ID: objectFormat.MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
name: "README.md",
entryMode: EntryModeBlob,
size: 8218,
sized: true,
},
{
ID: MustIDFromString("037f27dc9d353ae4fd50f0474b2194c593914e35"),
ID: objectFormat.MustIDFromString("037f27dc9d353ae4fd50f0474b2194c593914e35"),
name: "README_ZH.md",
entryMode: EntryModeBlob,
size: 4681,
sized: true,
},
{
ID: MustIDFromString("9846a94f7e8350a916632929d0fda38c90dd2ca8"),
ID: objectFormat.MustIDFromString("9846a94f7e8350a916632929d0fda38c90dd2ca8"),
name: "SECURITY.md",
entryMode: EntryModeBlob,
size: 429,
sized: true,
},
{
ID: MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
ID: objectFormat.MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
name: "assets",
entryMode: EntryModeTree,
sized: true,
Expand All @@ -54,7 +56,7 @@ func TestParseTreeEntriesLong(t *testing.T) {
},
}
for _, testCase := range testCases {
entries, err := ParseTreeEntries([]byte(testCase.Input))
entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input))
assert.NoError(t, err)
assert.Len(t, entries, len(testCase.Expected))
for i, entry := range entries {
Expand All @@ -64,6 +66,8 @@ func TestParseTreeEntriesLong(t *testing.T) {
}

func TestParseTreeEntriesShort(t *testing.T) {
objectFormat := ObjectFormatFromID(Sha1)

testCases := []struct {
Input string
Expected []*TreeEntry
Expand All @@ -74,20 +78,20 @@ func TestParseTreeEntriesShort(t *testing.T) {
`,
Expected: []*TreeEntry{
{
ID: MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
ID: objectFormat.MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
name: "README.md",
entryMode: EntryModeBlob,
},
{
ID: MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
ID: objectFormat.MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
name: "assets",
entryMode: EntryModeTree,
},
},
},
}
for _, testCase := range testCases {
entries, err := ParseTreeEntries([]byte(testCase.Input))
entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input))
assert.NoError(t, err)
assert.Len(t, entries, len(testCase.Expected))
for i, entry := range entries {
Expand All @@ -98,7 +102,7 @@ func TestParseTreeEntriesShort(t *testing.T) {

func TestParseTreeEntriesInvalid(t *testing.T) {
// there was a panic: "runtime error: slice bounds out of range" when the input was invalid: #20315
entries, err := ParseTreeEntries([]byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af"))
entries, err := ParseTreeEntries(ObjectFormatFromID(Sha1), []byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af"))
assert.Error(t, err)
assert.Len(t, entries, 0)
}
14 changes: 10 additions & 4 deletions modules/git/pipeline/lfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/git"

gogit "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
)

Expand All @@ -26,7 +27,7 @@ type LFSResult struct {
SHA string
Summary string
When time.Time
ParentHashes []git.SHA1
ParentHashes []git.ObjectID
BranchName string
FullCommitName string
}
Expand All @@ -38,7 +39,7 @@ func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }

// FindLFSFile finds commits that contain a provided pointer file hash
func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
resultsMap := map[string]*LFSResult{}
results := make([]*LFSResult, 0)

Expand All @@ -65,13 +66,18 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
if err == io.EOF {
break
}
if entry.Hash == hash {
if entry.Hash == plumbing.Hash(objectID.RawValue()) {
parents := make([]git.ObjectID, len(gitCommit.ParentHashes))
for i, parentCommitID := range gitCommit.ParentHashes {
parents[i] = git.ParseGogitHash(parentCommitID)
}

result := LFSResult{
Name: name,
SHA: gitCommit.Hash.String(),
Summary: strings.Split(strings.TrimSpace(gitCommit.Message), "\n")[0],
When: gitCommit.Author.When,
ParentHashes: gitCommit.ParentHashes,
ParentHashes: parents,
}
resultsMap[gitCommit.Hash.String()+":"+name] = &result
}
Expand Down
39 changes: 21 additions & 18 deletions modules/git/pipeline/lfs_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type LFSResult struct {
SHA string
Summary string
When time.Time
ParentHashes []git.SHA1
ParentIDs []git.ObjectID
BranchName string
FullCommitName string
}
Expand All @@ -36,7 +36,7 @@ func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }

// FindLFSFile finds commits that contain a provided pointer file hash
func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
resultsMap := map[string]*LFSResult{}
results := make([]*LFSResult, 0)

Expand Down Expand Up @@ -75,7 +75,7 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {

fnameBuf := make([]byte, 4096)
modeBuf := make([]byte, 40)
workingShaBuf := make([]byte, 20)
workingShaBuf := make([]byte, objectID.Type().FullLength()/2)

for scan.Scan() {
// Get the next commit ID
Expand Down Expand Up @@ -115,40 +115,43 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
continue
case "commit":
// Read in the commit to get its tree and in case this is one of the last used commits
curCommit, err = git.CommitFromReader(repo, git.MustIDFromString(string(commitID)), io.LimitReader(batchReader, size))
objectFormat, err := repo.GetObjectFormat()
if err != nil {
return nil, err
}
curCommit, err = git.CommitFromReader(repo, objectFormat.MustIDFromString(string(commitID)), io.LimitReader(batchReader, size))
if err != nil {
return nil, err
}
if _, err := batchReader.Discard(1); err != nil {
return nil, err
}

_, err := batchStdinWriter.Write([]byte(curCommit.Tree.ID.String() + "\n"))
if err != nil {
if _, err := batchStdinWriter.Write([]byte(curCommit.Tree.ID.String() + "\n")); err != nil {
return nil, err
}
curPath = ""
case "tree":
var n int64
for n < size {
mode, fname, sha20byte, count, err := git.ParseTreeLine(batchReader, modeBuf, fnameBuf, workingShaBuf)
mode, fname, binObjectID, count, err := git.ParseTreeLine(objectID.Type(), batchReader, modeBuf, fnameBuf, workingShaBuf)
if err != nil {
return nil, err
}
n += int64(count)
if bytes.Equal(sha20byte, hash[:]) {
if bytes.Equal(binObjectID, objectID.RawValue()) {
result := LFSResult{
Name: curPath + string(fname),
SHA: curCommit.ID.String(),
Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
When: curCommit.Author.When,
ParentHashes: curCommit.Parents,
Name: curPath + string(fname),
SHA: curCommit.ID.String(),
Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
When: curCommit.Author.When,
ParentIDs: curCommit.Parents,
}
resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result
} else if string(mode) == git.EntryModeTree.String() {
sha40Byte := make([]byte, 40)
git.To40ByteSHA(sha20byte, sha40Byte)
trees = append(trees, sha40Byte)
hexObjectID := make([]byte, objectID.Type().FullLength())
git.BinToHex(objectID.Type(), binObjectID, hexObjectID)
trees = append(trees, hexObjectID)
paths = append(paths, curPath+string(fname)+"/")
}
}
Expand Down Expand Up @@ -180,8 +183,8 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {

for _, result := range resultsMap {
hasParent := false
for _, parentHash := range result.ParentHashes {
if _, hasParent = resultsMap[parentHash.String()+":"+result.Name]; hasParent {
for _, parentID := range result.ParentIDs {
if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent {
break
}
}
Expand Down
4 changes: 2 additions & 2 deletions modules/git/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func SanitizeRefPattern(name string) string {
type Reference struct {
Name string
repo *Repository
Object SHA1 // The id of this commit object
Object ObjectID // The id of this commit object
Type string
}

Expand Down Expand Up @@ -205,7 +205,7 @@ func RefURL(repoURL, ref string) string {
return repoURL + "/src/branch/" + refName
case refFullName.IsTag():
return repoURL + "/src/tag/" + refName
case !IsValidSHAPattern(ref):
case !ObjectFormatFromID(Sha1).IsValid(ref):
// assume they mean a branch
return repoURL + "/src/branch/" + refName
default:
Expand Down
31 changes: 29 additions & 2 deletions modules/git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package git
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/url"
Expand Down Expand Up @@ -62,14 +63,40 @@ func IsRepoURLAccessible(ctx context.Context, url string) bool {
return err == nil
}

// GetObjectFormatOfRepo returns the hash type of a repository at a given path
func GetObjectFormatOfRepo(ctx context.Context, repoPath string) (ObjectFormat, error) {
var stdout, stderr strings.Builder

err := NewCommand(ctx, "hash-object", "--stdin").Run(&RunOpts{
Dir: repoPath,
Stdout: &stdout,
Stderr: &stderr,
Stdin: &strings.Reader{},
})
if err != nil {
return nil, err
}

if stderr.Len() > 0 {
return nil, errors.New(stderr.String())
}

h, err := IDFromString(strings.TrimRight(stdout.String(), "\n"))
if err != nil {
return nil, err
}

return h.Type(), nil
}

// InitRepository initializes a new Git repository.
func InitRepository(ctx context.Context, repoPath string, bare bool) error {
func InitRepository(ctx context.Context, repoPath string, bare bool, objectFormat ObjectFormat) error {
err := os.MkdirAll(repoPath, os.ModePerm)
if err != nil {
return err
}

cmd := NewCommand(ctx, "init")
cmd := NewCommand(ctx, "init", "--object-format").AddDynamicArguments(objectFormat.String())
if bare {
cmd.AddArguments("--bare")
}
Expand Down
3 changes: 3 additions & 0 deletions modules/git/repo_base_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/go-git/go-billy/v5/osfs"
gogit "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/storage/filesystem"
)
Expand All @@ -32,6 +33,7 @@ type Repository struct {

Ctx context.Context
LastCommitCache *LastCommitCache
objectFormat ObjectFormat
}

// openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
Expand Down Expand Up @@ -68,6 +70,7 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) {
gogitStorage: storage,
tagCache: newObjectCache(),
Ctx: ctx,
objectFormat: ParseGogitHash(plumbing.ZeroHash).Type(),
}, nil
}

Expand Down
7 changes: 7 additions & 0 deletions modules/git/repo_base_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type Repository struct {

Ctx context.Context
LastCommitCache *LastCommitCache

objectFormat ObjectFormat
}

// openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
Expand Down Expand Up @@ -63,6 +65,11 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) {
repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath)
repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repoPath)

repo.objectFormat, err = repo.GetObjectFormat()
if err != nil {
return nil, err
}

return repo, nil
}

Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package git

// GetBlob finds the blob object in the repository.
func (repo *Repository) GetBlob(idStr string) (*Blob, error) {
id, err := NewIDFromString(idStr)
id, err := repo.objectFormat.NewIDFromString(idStr)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions modules/git/repo_blob_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/go-git/go-git/v5/plumbing"
)

func (repo *Repository) getBlob(id SHA1) (*Blob, error) {
encodedObj, err := repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, id)
func (repo *Repository) getBlob(id ObjectID) (*Blob, error) {
encodedObj, err := repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, plumbing.Hash(id.RawValue()))
if err != nil {
return nil, ErrNotExist{id.String(), ""}
}
Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo_blob_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package git

func (repo *Repository) getBlob(id SHA1) (*Blob, error) {
func (repo *Repository) getBlob(id ObjectID) (*Blob, error) {
if id.IsZero() {
return nil, ErrNotExist{id.String(), ""}
}
Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo_blob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestRepository_GetBlob_NoId(t *testing.T) {
defer r.Close()

testCase := ""
testError := fmt.Errorf("Length must be 40: %s", testCase)
testError := fmt.Errorf("length must be 40: %s", testCase)

blob, err := r.GetBlob(testCase)
assert.Nil(t, blob)
Expand Down
35 changes: 15 additions & 20 deletions modules/git/repo_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package git

import (
"bytes"
"encoding/hex"
"fmt"
"io"
"strconv"
"strings"
Expand All @@ -28,7 +26,7 @@ func (repo *Repository) GetTagCommitID(name string) (string, error) {

// GetCommit returns commit object of by ID string.
func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
id, err := repo.ConvertToSHA1(commitID)
id, err := repo.ConvertToGitID(commitID)
if err != nil {
return nil, err
}
Expand All @@ -54,7 +52,7 @@ func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
return repo.GetCommit(commitID)
}

func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit, error) {
func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Commit, error) {
// File name starts with ':' must be escaped.
if relpath[0] == ':' {
relpath = `\` + relpath
Expand All @@ -65,7 +63,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
return nil, runErr
}

id, err := NewIDFromString(stdout)
id, err := repo.objectFormat.NewIDFromString(stdout)
if err != nil {
return nil, err
}
Expand All @@ -90,7 +88,7 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
return commits[0], nil
}

func (repo *Repository) commitsByRange(id SHA1, page, pageSize int, not string) ([]*Commit, error) {
func (repo *Repository) commitsByRange(id ObjectID, page, pageSize int, not string) ([]*Commit, error) {
cmd := NewCommand(repo.Ctx, "log").
AddOptionFormat("--skip=%d", (page-1)*pageSize).
AddOptionFormat("--max-count=%d", pageSize).
Expand All @@ -109,7 +107,7 @@ func (repo *Repository) commitsByRange(id SHA1, page, pageSize int, not string)
return repo.parsePrettyFormatLogToList(stdout)
}

func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Commit, error) {
func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([]*Commit, error) {
// add common arguments to git command
addCommonSearchArgs := func(c *Command) {
// ignore case
Expand Down Expand Up @@ -164,7 +162,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
// then let's iterate over them
for _, v := range opts.Keywords {
// ignore anything not matching a valid sha pattern
if IsValidSHAPattern(v) {
if id.Type().IsValid(v) {
// create new git log command with 1 commit limit
hashCmd := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat)
// add previous arguments except for --grep and --all
Expand Down Expand Up @@ -245,25 +243,22 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
}
}()

len := repo.objectFormat.FullLength()
commits := []*Commit{}
shaline := [41]byte{}
var sha1 SHA1
shaline := make([]byte, len+1)
for {
n, err := io.ReadFull(stdoutReader, shaline[:])
if err != nil || n < 40 {
n, err := io.ReadFull(stdoutReader, shaline)
if err != nil || n < len {
if err == io.EOF {
err = nil
}
return commits, err
}
n, err = hex.Decode(sha1[:], shaline[0:40])
if n != 20 {
err = fmt.Errorf("invalid sha %q", string(shaline[:40]))
}
objectID, err := repo.objectFormat.NewIDFromString(string(shaline[0:len]))
if err != nil {
return nil, err
}
commit, err := repo.getCommit(sha1)
commit, err := repo.getCommit(objectID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -392,7 +387,7 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
}

// commitsBefore the limit is depth, not total number of returned commits.
func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) {
func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) {
cmd := NewCommand(repo.Ctx, "log", prettyLogFormat)
if limit > 0 {
cmd.AddOptionFormat("-%d", limit)
Expand Down Expand Up @@ -426,11 +421,11 @@ func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) {
return commits, nil
}

func (repo *Repository) getCommitsBefore(id SHA1) ([]*Commit, error) {
func (repo *Repository) getCommitsBefore(id ObjectID) ([]*Commit, error) {
return repo.commitsBefore(id, 0)
}

func (repo *Repository) getCommitsBeforeLimit(id SHA1, num int) ([]*Commit, error) {
func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit, error) {
return repo.commitsBefore(id, num)
}

Expand Down
35 changes: 21 additions & 14 deletions modules/git/repo_commit_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/plumbing/object"
)

Expand Down Expand Up @@ -38,40 +39,46 @@ func (repo *Repository) RemoveReference(name string) error {
return repo.gogitRepo.Storer.RemoveReference(plumbing.ReferenceName(name))
}

// ConvertToSHA1 returns a Hash object from a potential ID string
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
if len(commitID) == SHAFullLength {
sha1, err := NewIDFromString(commitID)
// ConvertToHash returns a Hash object from a potential ID string
func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) {
objectFormat := repo.objectFormat
if len(commitID) == hash.HexSize && objectFormat.IsValid(commitID) {
ID, err := objectFormat.NewIDFromString(commitID)
if err == nil {
return sha1, nil
return ID, nil
}
}

actualCommitID, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(&RunOpts{Dir: repo.Path})
actualCommitID = strings.TrimSpace(actualCommitID)
if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") ||
strings.Contains(err.Error(), "fatal: Needed a single revision") {
return SHA1{}, ErrNotExist{commitID, ""}
return objectFormat.Empty(), ErrNotExist{commitID, ""}
}
return SHA1{}, err
return objectFormat.Empty(), err
}

return NewIDFromString(actualCommitID)
return objectFormat.NewIDFromString(actualCommitID)
}

// IsCommitExist returns true if given commit exists in current repository.
func (repo *Repository) IsCommitExist(name string) bool {
hash := plumbing.NewHash(name)
_, err := repo.gogitRepo.CommitObject(hash)
hash, err := repo.ConvertToGitID(name)
if err != nil {
return false
}
_, err = repo.gogitRepo.CommitObject(plumbing.Hash(hash.RawValue()))
return err == nil
}

func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
func (repo *Repository) getCommit(id ObjectID) (*Commit, error) {
var tagObject *object.Tag

gogitCommit, err := repo.gogitRepo.CommitObject(id)
commitID := plumbing.Hash(id.RawValue())
gogitCommit, err := repo.gogitRepo.CommitObject(commitID)
if err == plumbing.ErrObjectNotFound {
tagObject, err = repo.gogitRepo.TagObject(id)
tagObject, err = repo.gogitRepo.TagObject(commitID)
if err == plumbing.ErrObjectNotFound {
return nil, ErrNotExist{
ID: id.String(),
Expand All @@ -94,7 +101,7 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
return nil, err
}

commit.Tree.ID = tree.Hash
commit.Tree.ID = ParseGogitHash(tree.Hash)
commit.Tree.gogitTree = tree

return commit, nil
Expand Down
25 changes: 13 additions & 12 deletions modules/git/repo_commit_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (repo *Repository) IsCommitExist(name string) bool {
return err == nil
}

func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
func (repo *Repository) getCommit(id ObjectID) (*Commit, error) {
wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel()

Expand All @@ -74,7 +74,7 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
return repo.getCommitFromBatchReader(rd, id)
}

func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id SHA1) (*Commit, error) {
func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id ObjectID) (*Commit, error) {
_, typ, size, err := ReadBatchLine(rd)
if err != nil {
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
Expand All @@ -97,7 +97,7 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id SHA1) (*Co
if err != nil {
return nil, err
}
tag, err := parseTagData(data)
tag, err := parseTagData(id.Type(), data)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -131,28 +131,29 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id SHA1) (*Co
}
}

// ConvertToSHA1 returns a Hash object from a potential ID string
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
if len(commitID) == SHAFullLength && IsValidSHAPattern(commitID) {
sha1, err := NewIDFromString(commitID)
// ConvertToGitID returns a GitHash object from a potential ID string
func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) {
IDType := repo.objectFormat
if len(commitID) == IDType.FullLength() && IDType.IsValid(commitID) {
ID, err := repo.objectFormat.NewIDFromString(commitID)
if err == nil {
return sha1, nil
return ID, nil
}
}

wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel()
_, err := wr.Write([]byte(commitID + "\n"))
if err != nil {
return SHA1{}, err
return nil, err
}
sha, _, _, err := ReadBatchLine(rd)
if err != nil {
if IsErrNotExist(err) {
return SHA1{}, ErrNotExist{commitID, ""}
return nil, ErrNotExist{commitID, ""}
}
return SHA1{}, err
return nil, err
}

return MustIDFromString(string(sha)), nil
return repo.objectFormat.MustIDFromString(string(sha)), nil
}
2 changes: 1 addition & 1 deletion modules/git/repo_compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
// If base is the SHA of an empty tree (EmptyTreeSHA), it returns the files changes from the initial commit to the head commit
func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) {
cmd := NewCommand(repo.Ctx, "diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z")
if base == EmptySHA {
if base == repo.objectFormat.Empty().String() {
cmd.AddDynamicArguments(head)
} else {
cmd.AddDynamicArguments(base, head)
Expand Down
6 changes: 3 additions & 3 deletions modules/git/repo_compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@ func TestGetCommitFilesChanged(t *testing.T) {
files []string
}{
{
EmptySHA,
repo.objectFormat.Empty().String(),
"95bb4d39648ee7e325106df01a621c530863a653",
[]string{"file1.txt"},
},
{
EmptySHA,
repo.objectFormat.Empty().String(),
"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
[]string{"file2.txt"},
},
Expand All @@ -146,7 +146,7 @@ func TestGetCommitFilesChanged(t *testing.T) {
[]string{"file2.txt"},
},
{
EmptyTreeSHA,
repo.objectFormat.EmptyTree().String(),
"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
[]string{"file1.txt", "file2.txt"},
},
Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo_gpg.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (gpgSettings *GPGSettings) LoadPublicKeyContent() error {
"gpg -a --export",
"gpg", "-a", "--export", gpgSettings.KeyID)
if err != nil {
return fmt.Errorf("Unable to get default signing key: %s, %s, %w", gpgSettings.KeyID, stderr, err)
return fmt.Errorf("unable to get default signing key: %s, %s, %w", gpgSettings.KeyID, stderr, err)
}
gpgSettings.PublicKeyContent = content
return nil
Expand Down
19 changes: 13 additions & 6 deletions modules/git/repo_index.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import (

// ReadTreeToIndex reads a treeish to the index
func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
if len(treeish) != SHAFullLength {
objectFormat, err := repo.GetObjectFormat()
if err != nil {
return err
}

if len(treeish) != objectFormat.FullLength() {
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return err
Expand All @@ -25,14 +30,14 @@ func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string)
treeish = res[:len(res)-1]
}
}
id, err := NewIDFromString(treeish)
id, err := objectFormat.NewIDFromString(treeish)
if err != nil {
return err
}
return repo.readTreeToIndex(id, indexFilename...)
}

func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error {
func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) error {
var env []string
if len(indexFilename) > 0 {
env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
Expand Down Expand Up @@ -95,7 +100,9 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
buffer := new(bytes.Buffer)
for _, file := range filenames {
if file != "" {
buffer.WriteString("0 0000000000000000000000000000000000000000\t")
buffer.WriteString("0 ")
buffer.WriteString(repo.objectFormat.Empty().String())
buffer.WriteByte('\t')
buffer.WriteString(file)
buffer.WriteByte('\000')
}
Expand All @@ -109,7 +116,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
}

// AddObjectToIndex adds the provided object hash to the index at the provided filename
func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error {
func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename string) error {
cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, object.String(), filename)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
return err
Expand All @@ -121,7 +128,7 @@ func (repo *Repository) WriteTree() (*Tree, error) {
if runErr != nil {
return nil, runErr
}
id, err := NewIDFromString(strings.TrimSpace(stdout))
id, err := repo.objectFormat.NewIDFromString(strings.TrimSpace(stdout))
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo_language_stats_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
return nil, ErrNotExist{commitID, ""}
}

sha, err := NewIDFromString(string(shaBytes))
sha, err := repo.objectFormat.NewIDFromString(string(shaBytes))
if err != nil {
log.Debug("Unable to get commit for: %s. Err: %v", commitID, err)
return nil, ErrNotExist{commitID, ""}
Expand Down
44 changes: 37 additions & 7 deletions modules/git/repo_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,47 @@ func (o ObjectType) Bytes() []byte {
return []byte(o)
}

// HashObject takes a reader and returns SHA1 hash for that reader
func (repo *Repository) HashObject(reader io.Reader) (SHA1, error) {
idStr, err := repo.hashObject(reader)
type EmptyReader struct{}

func (EmptyReader) Read(p []byte) (int, error) {
return 0, io.EOF
}

func (repo *Repository) GetObjectFormat() (ObjectFormat, error) {
if repo != nil && repo.objectFormat != nil {
return repo.objectFormat, nil
}

str, err := repo.hashObject(EmptyReader{}, false)
if err != nil {
return nil, err
}
hash, err := IDFromString(str)
if err != nil {
return SHA1{}, err
return nil, err
}
return NewIDFromString(idStr)

repo.objectFormat = hash.Type()

return repo.objectFormat, nil
}

func (repo *Repository) hashObject(reader io.Reader) (string, error) {
cmd := NewCommand(repo.Ctx, "hash-object", "-w", "--stdin")
// HashObject takes a reader and returns hash for that reader
func (repo *Repository) HashObject(reader io.Reader) (ObjectID, error) {
idStr, err := repo.hashObject(reader, true)
if err != nil {
return nil, err
}
return repo.objectFormat.NewIDFromString(idStr)
}

func (repo *Repository) hashObject(reader io.Reader, save bool) (string, error) {
var cmd *Command
if save {
cmd = NewCommand(repo.Ctx, "hash-object", "-w", "--stdin")
} else {
cmd = NewCommand(repo.Ctx, "hash-object", "--stdin")
}
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
err := cmd.Run(&RunOpts{
Expand Down
4 changes: 2 additions & 2 deletions modules/git/repo_ref_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
refType := string(ObjectCommit)
if ref.Name().IsTag() {
// tags can be of type `commit` (lightweight) or `tag` (annotated)
if tagType, _ := repo.GetTagType(ref.Hash()); err == nil {
if tagType, _ := repo.GetTagType(ParseGogitHash(ref.Hash())); err == nil {
refType = tagType
}
}
r := &Reference{
Name: ref.Name().String(),
Object: ref.Hash(),
Object: ParseGogitHash(ref.Hash()),
Type: refType,
repo: repo,
}
Expand Down
2 changes: 1 addition & 1 deletion modules/git/repo_ref_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
if pattern == "" || strings.HasPrefix(refName, pattern) {
r := &Reference{
Name: refName,
Object: MustIDFromString(sha),
Object: repo.objectFormat.MustIDFromString(sha),
Type: typ,
repo: repo,
}
Expand Down
14 changes: 7 additions & 7 deletions modules/git/repo_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
return nil, err
}

id, err := NewIDFromString(idStr)
id, err := repo.objectFormat.NewIDFromString(idStr)
if err != nil {
return nil, err
}
Expand All @@ -98,7 +98,7 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {

// GetTagWithID returns a Git tag by given name and ID
func (repo *Repository) GetTagWithID(idStr, name string) (*Tag, error) {
id, err := NewIDFromString(idStr)
id, err := repo.objectFormat.NewIDFromString(idStr)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -139,7 +139,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
break
}

tag, err := parseTagRef(ref)
tag, err := parseTagRef(repo.objectFormat, ref)
if err != nil {
return nil, 0, fmt.Errorf("GetTagInfos: parse tag: %w", err)
}
Expand All @@ -159,13 +159,13 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
}

// parseTagRef parses a tag from a 'git for-each-ref'-produced reference.
func parseTagRef(ref map[string]string) (tag *Tag, err error) {
func parseTagRef(objectFormat ObjectFormat, ref map[string]string) (tag *Tag, err error) {
tag = &Tag{
Type: ref["objecttype"],
Name: ref["refname:short"],
}

tag.ID, err = NewIDFromString(ref["objectname"])
tag.ID, err = objectFormat.NewIDFromString(ref["objectname"])
if err != nil {
return nil, fmt.Errorf("parse objectname '%s': %w", ref["objectname"], err)
}
Expand All @@ -175,7 +175,7 @@ func parseTagRef(ref map[string]string) (tag *Tag, err error) {
tag.Object = tag.ID
} else {
// annotated tag
tag.Object, err = NewIDFromString(ref["object"])
tag.Object, err = objectFormat.NewIDFromString(ref["object"])
if err != nil {
return nil, fmt.Errorf("parse object '%s': %w", ref["object"], err)
}
Expand Down Expand Up @@ -208,7 +208,7 @@ func parseTagRef(ref map[string]string) (tag *Tag, err error) {

// GetAnnotatedTag returns a Git tag by its SHA, must be an annotated tag
func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
id, err := NewIDFromString(sha)
id, err := repo.objectFormat.NewIDFromString(sha)
if err != nil {
return nil, err
}
Expand Down
12 changes: 6 additions & 6 deletions modules/git/repo_tag_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ func (repo *Repository) GetTags(skip, limit int) ([]string, error) {
}

// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
func (repo *Repository) GetTagType(id SHA1) (string, error) {
func (repo *Repository) GetTagType(id ObjectID) (string, error) {
// Get tag type
obj, err := repo.gogitRepo.Object(plumbing.AnyObject, id)
obj, err := repo.gogitRepo.Object(plumbing.AnyObject, plumbing.Hash(id.RawValue()))
if err != nil {
if err == plumbing.ErrReferenceNotFound {
return "", &ErrNotExist{ID: id.String()}
Expand All @@ -68,7 +68,7 @@ func (repo *Repository) GetTagType(id SHA1) (string, error) {
return obj.Type().String(), nil
}

func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
func (repo *Repository) getTag(tagID ObjectID, name string) (*Tag, error) {
t, ok := repo.tagCache.Get(tagID.String())
if ok {
log.Debug("Hit cache: %s", tagID)
Expand All @@ -88,7 +88,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
// every tag should have a commit ID so return all errors
return nil, err
}
commitID, err := NewIDFromString(commitIDStr)
commitID, err := IDFromString(commitIDStr)
if err != nil {
return nil, err
}
Expand All @@ -112,7 +112,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
return tag, nil
}

gogitTag, err := repo.gogitRepo.TagObject(tagID)
gogitTag, err := repo.gogitRepo.TagObject(plumbing.Hash(tagID.RawValue()))
if err != nil {
if err == plumbing.ErrReferenceNotFound {
return nil, &ErrNotExist{ID: tagID.String()}
Expand All @@ -124,7 +124,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
tag := &Tag{
Name: name,
ID: tagID,
Object: gogitTag.Target,
Object: commitID.Type().MustID(gogitTag.Target[:]),
Type: tp,
Tagger: &gogitTag.Tagger,
Message: gogitTag.Message,
Expand Down
8 changes: 4 additions & 4 deletions modules/git/repo_tag_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
}

// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
func (repo *Repository) GetTagType(id SHA1) (string, error) {
func (repo *Repository) GetTagType(id ObjectID) (string, error) {
wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel()
_, err := wr.Write([]byte(id.String() + "\n"))
Expand All @@ -44,7 +44,7 @@ func (repo *Repository) GetTagType(id SHA1) (string, error) {
return typ, nil
}

func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
func (repo *Repository) getTag(tagID ObjectID, name string) (*Tag, error) {
t, ok := repo.tagCache.Get(tagID.String())
if ok {
log.Debug("Hit cache: %s", tagID)
Expand All @@ -64,7 +64,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
// every tag should have a commit ID so return all errors
return nil, err
}
commitID, err := NewIDFromString(commitIDStr)
commitID, err := repo.objectFormat.NewIDFromString(commitIDStr)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -117,7 +117,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
return nil, err
}

tag, err := parseTagData(data)
tag, err := parseTagData(tagID.Type(), data)
if err != nil {
return nil, err
}
Expand Down
15 changes: 8 additions & 7 deletions modules/git/repo_tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ func TestRepository_GetAnnotatedTag(t *testing.T) {
}

func TestRepository_parseTagRef(t *testing.T) {
sha1 := ObjectFormatFromID(Sha1)
tests := []struct {
name string

Expand Down Expand Up @@ -223,8 +224,8 @@ func TestRepository_parseTagRef(t *testing.T) {

want: &Tag{
Name: "v1.9.1",
ID: MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
Object: MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
ID: sha1.MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
Object: sha1.MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
Type: "commit",
Tagger: parseAuthorLine(t, "Foo Bar <foo@bar.com> 1565789218 +0300"),
Message: "Add changelog of v1.9.1 (#7859)\n\n* add changelog of v1.9.1\n* Update CHANGELOG.md\n",
Expand Down Expand Up @@ -252,8 +253,8 @@ func TestRepository_parseTagRef(t *testing.T) {

want: &Tag{
Name: "v0.0.1",
ID: MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
Object: MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
ID: sha1.MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
Object: sha1.MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
Type: "tag",
Tagger: parseAuthorLine(t, "Foo Bar <foo@bar.com> 1565789218 +0300"),
Message: "Add changelog of v1.9.1 (#7859)\n\n* add changelog of v1.9.1\n* Update CHANGELOG.md\n",
Expand Down Expand Up @@ -310,8 +311,8 @@ qbHDASXl

want: &Tag{
Name: "v0.0.1",
ID: MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
Object: MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
ID: sha1.MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
Object: sha1.MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
Type: "tag",
Tagger: parseAuthorLine(t, "Foo Bar <foo@bar.com> 1565789218 +0300"),
Message: "Add changelog of v1.9.1 (#7859)\n\n* add changelog of v1.9.1\n* Update CHANGELOG.md",
Expand Down Expand Up @@ -350,7 +351,7 @@ Add changelog of v1.9.1 (#7859)
for _, test := range tests {
tc := test // don't close over loop variable
t.Run(tc.name, func(t *testing.T) {
got, err := parseTagRef(tc.givenRef)
got, err := parseTagRef(sha1, tc.givenRef)

if tc.wantErr {
require.Error(t, err)
Expand Down
6 changes: 3 additions & 3 deletions modules/git/repo_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type CommitTreeOpts struct {
}

// CommitTree creates a commit from a given tree id for the user with provided message
func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) {
func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opts CommitTreeOpts) (ObjectID, error) {
commitTimeStr := time.Now().Format(time.RFC3339)

// Because this may call hooks we should pass in the environment
Expand Down Expand Up @@ -61,7 +61,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
Stderr: stderr,
})
if err != nil {
return SHA1{}, ConcatenateError(err, stderr.String())
return nil, ConcatenateError(err, stderr.String())
}
return NewIDFromString(strings.TrimSpace(stdout.String()))
return repo.objectFormat.NewIDFromString(strings.TrimSpace(stdout.String()))
}
14 changes: 8 additions & 6 deletions modules/git/repo_tree_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

package git

func (repo *Repository) getTree(id SHA1) (*Tree, error) {
gogitTree, err := repo.gogitRepo.TreeObject(id)
import "github.com/go-git/go-git/v5/plumbing"

func (repo *Repository) getTree(id ObjectID) (*Tree, error) {
gogitTree, err := repo.gogitRepo.TreeObject(plumbing.Hash(id.RawValue()))
if err != nil {
return nil, err
}
Expand All @@ -19,7 +21,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {

// GetTree find the tree object in the repository.
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
if len(idStr) != SHAFullLength {
if len(idStr) != repo.objectFormat.FullLength() {
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
Expand All @@ -28,14 +30,14 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
idStr = res[:len(res)-1]
}
}
id, err := NewIDFromString(idStr)
id, err := repo.objectFormat.NewIDFromString(idStr)
if err != nil {
return nil, err
}
resolvedID := id
commitObject, err := repo.gogitRepo.CommitObject(id)
commitObject, err := repo.gogitRepo.CommitObject(plumbing.Hash(id.RawValue()))
if err == nil {
id = SHA1(commitObject.TreeHash)
id = ParseGogitHash(commitObject.TreeHash)
}
treeObject, err := repo.getTree(id)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions modules/git/repo_tree_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"io"
)

func (repo *Repository) getTree(id SHA1) (*Tree, error) {
func (repo *Repository) getTree(id ObjectID) (*Tree, error) {
wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel()

Expand All @@ -28,7 +28,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
if err != nil {
return nil, err
}
tag, err := parseTagData(data)
tag, err := parseTagData(id.Type(), data)
if err != nil {
return nil, err
}
Expand All @@ -51,7 +51,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
case "tree":
tree := NewTree(repo, id)
tree.ResolvedID = id
tree.entries, err = catBatchParseTreeEntries(tree, rd, size)
tree.entries, err = catBatchParseTreeEntries(repo.objectFormat, tree, rd, size)
if err != nil {
return nil, err
}
Expand All @@ -66,7 +66,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {

// GetTree find the tree object in the repository.
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
if len(idStr) != SHAFullLength {
if len(idStr) != repo.objectFormat.FullLength() {
res, err := repo.GetRefCommitID(idStr)
if err != nil {
return nil, err
Expand All @@ -75,7 +75,7 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
idStr = res
}
}
id, err := NewIDFromString(idStr)
id, err := repo.objectFormat.NewIDFromString(idStr)
if err != nil {
return nil, err
}
Expand Down
72 changes: 0 additions & 72 deletions modules/git/sha1.go

This file was deleted.

19 changes: 0 additions & 19 deletions modules/git/sha1_gogit.go

This file was deleted.

61 changes: 0 additions & 61 deletions modules/git/sha1_nogogit.go

This file was deleted.

20 changes: 0 additions & 20 deletions modules/git/sha1_test.go

This file was deleted.

10 changes: 6 additions & 4 deletions modules/git/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const (
// Tag represents a Git tag.
type Tag struct {
Name string
ID SHA1
Object SHA1 // The id of this commit object
ID ObjectID
Object ObjectID // The id of this commit object
Type string
Tagger *Signature
Message string
Expand All @@ -33,8 +33,10 @@ func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) {
// Parse commit information from the (uncompressed) raw
// data from the commit object.
// \n\n separate headers from message
func parseTagData(data []byte) (*Tag, error) {
func parseTagData(objectFormat ObjectFormat, data []byte) (*Tag, error) {
tag := new(Tag)
tag.ID = objectFormat.NewEmptyID()
tag.Object = objectFormat.NewEmptyID()
tag.Tagger = &Signature{}
// we now have the contents of the commit object. Let's investigate...
nextline := 0
Expand All @@ -48,7 +50,7 @@ l:
reftype := line[:spacepos]
switch string(reftype) {
case "object":
id, err := NewIDFromString(string(line[spacepos+1:]))
id, err := objectFormat.NewIDFromString(string(line[spacepos+1:]))
if err != nil {
return nil, err
}
Expand Down
10 changes: 5 additions & 5 deletions modules/git/tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ tagger Lucas Michot <lucas@semalead.com> 1484491741 +0100
`), tag: Tag{
Name: "",
ID: SHA1{},
Object: SHA1{0x3b, 0x11, 0x4a, 0xb8, 0x0, 0xc6, 0x43, 0x2a, 0xd4, 0x23, 0x87, 0xcc, 0xf6, 0xbc, 0x8d, 0x43, 0x88, 0xa2, 0x88, 0x5a},
ID: NewSha1(),
Object: &Sha1Hash{0x3b, 0x11, 0x4a, 0xb8, 0x0, 0xc6, 0x43, 0x2a, 0xd4, 0x23, 0x87, 0xcc, 0xf6, 0xbc, 0x8d, 0x43, 0x88, 0xa2, 0x88, 0x5a},
Type: "commit",
Tagger: &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484491741, 0)},
Message: "",
Expand All @@ -39,8 +39,8 @@ o
ono`), tag: Tag{
Name: "",
ID: SHA1{},
Object: SHA1{0x7c, 0xdf, 0x42, 0xc0, 0xb1, 0xcc, 0x76, 0x3a, 0xb7, 0xe4, 0xc3, 0x3c, 0x47, 0xa2, 0x4e, 0x27, 0xc6, 0x6b, 0xfc, 0xcc},
ID: NewSha1(),
Object: &Sha1Hash{0x7c, 0xdf, 0x42, 0xc0, 0xb1, 0xcc, 0x76, 0x3a, 0xb7, 0xe4, 0xc3, 0x3c, 0x47, 0xa2, 0x4e, 0x27, 0xc6, 0x6b, 0xfc, 0xcc},
Type: "commit",
Tagger: &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484553735, 0)},
Message: "test message\no\n\nono",
Expand All @@ -49,7 +49,7 @@ ono`), tag: Tag{
}

for _, test := range testData {
tag, err := parseTagData(test.data)
tag, err := parseTagData(ObjectFormatFromID(Sha1), test.data)
assert.NoError(t, err)
assert.EqualValues(t, test.tag.ID, tag.ID)
assert.EqualValues(t, test.tag.Object, tag.Object)
Expand Down
2 changes: 1 addition & 1 deletion modules/git/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

// NewTree create a new tree according the repository and tree id
func NewTree(repo *Repository, id SHA1) *Tree {
func NewTree(repo *Repository, id ObjectID) *Tree {
return &Tree{
ID: id,
repo: repo,
Expand Down
2 changes: 1 addition & 1 deletion modules/git/tree_blob_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
gogitTreeEntry: &object.TreeEntry{
Name: "",
Mode: filemode.Dir,
Hash: t.ID,
Hash: plumbing.Hash(t.ID.RawValue()),
},
}, nil
}
Expand Down
4 changes: 2 additions & 2 deletions modules/git/tree_entry_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

// TreeEntry the leaf in the git tree
type TreeEntry struct {
ID SHA1
ID ObjectID

gogitTreeEntry *object.TreeEntry
ptree *Tree
Expand Down Expand Up @@ -88,7 +88,7 @@ func (te *TreeEntry) Blob() *Blob {
}

return &Blob{
ID: te.gogitTreeEntry.Hash,
ID: ParseGogitHash(te.gogitTreeEntry.Hash),
gogitEncodedObj: encodedObj,
name: te.Name(),
}
Expand Down
2 changes: 1 addition & 1 deletion modules/git/tree_entry_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "code.gitea.io/gitea/modules/log"

// TreeEntry the leaf in the git tree
type TreeEntry struct {
ID SHA1
ID ObjectID

ptree *Tree

Expand Down
10 changes: 5 additions & 5 deletions modules/git/tree_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (

// Tree represents a flat directory listing.
type Tree struct {
ID SHA1
ResolvedID SHA1
ID ObjectID
ResolvedID ObjectID
repo *Repository

gogitTree *object.Tree
Expand All @@ -26,7 +26,7 @@ type Tree struct {
}

func (t *Tree) loadTreeObject() error {
gogitTree, err := t.repo.gogitRepo.TreeObject(t.ID)
gogitTree, err := t.repo.gogitRepo.TreeObject(plumbing.Hash(t.ID.RawValue()))
if err != nil {
return err
}
Expand All @@ -47,7 +47,7 @@ func (t *Tree) ListEntries() (Entries, error) {
entries := make([]*TreeEntry, len(t.gogitTree.Entries))
for i, entry := range t.gogitTree.Entries {
entries[i] = &TreeEntry{
ID: entry.Hash,
ID: ParseGogitHash(entry.Hash),
gogitTreeEntry: &t.gogitTree.Entries[i],
ptree: t,
}
Expand Down Expand Up @@ -81,7 +81,7 @@ func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
}

convertedEntry := &TreeEntry{
ID: entry.Hash,
ID: ParseGogitHash(entry.Hash),
gogitTreeEntry: &entry,
ptree: t,
fullName: fullName,
Expand Down
10 changes: 5 additions & 5 deletions modules/git/tree_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (

// Tree represents a flat directory listing.
type Tree struct {
ID SHA1
ResolvedID SHA1
ID ObjectID
ResolvedID ObjectID
repo *Repository

// parent tree
Expand Down Expand Up @@ -54,7 +54,7 @@ func (t *Tree) ListEntries() (Entries, error) {
}
}
if typ == "tree" {
t.entries, err = catBatchParseTreeEntries(t, rd, sz)
t.entries, err = catBatchParseTreeEntries(t.ID.Type(), t, rd, sz)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -90,7 +90,7 @@ func (t *Tree) ListEntries() (Entries, error) {
}

var err error
t.entries, err = parseTreeEntries(stdout, t)
t.entries, err = parseTreeEntries(t.repo.objectFormat, stdout, t)
if err == nil {
t.entriesParsed = true
}
Expand All @@ -114,7 +114,7 @@ func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) {
}

var err error
t.entriesRecursive, err = parseTreeEntries(stdout, t)
t.entriesRecursive, err = parseTreeEntries(t.repo.objectFormat, stdout, t)
if err == nil {
t.entriesRecursiveParsed = true
}
Expand Down
17 changes: 13 additions & 4 deletions modules/indexer/code/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ func isIndexable(entry *git.TreeEntry) bool {
}

// parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command
func parseGitLsTreeOutput(stdout []byte) ([]internal.FileUpdate, error) {
entries, err := git.ParseTreeEntries(stdout)
func parseGitLsTreeOutput(objectFormat git.ObjectFormat, stdout []byte) ([]internal.FileUpdate, error) {
entries, err := git.ParseTreeEntries(objectFormat, stdout)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -92,7 +92,11 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s
}

var err error
changes.Updates, err = parseGitLsTreeOutput(stdout)
objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath())
if err != nil {
return nil, err
}
changes.Updates, err = parseGitLsTreeOutput(objectFormat, stdout)
return &changes, err
}

Expand Down Expand Up @@ -169,6 +173,11 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio
if err != nil {
return nil, err
}
changes.Updates, err = parseGitLsTreeOutput(lsTreeStdout)

objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath())
if err != nil {
return nil, err
}
changes.Updates, err = parseGitLsTreeOutput(objectFormat, lsTreeStdout)
return &changes, err
}
7 changes: 4 additions & 3 deletions modules/repository/commits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func TestCommitToPushCommit(t *testing.T) {
When: now,
}
const hexString = "0123456789abcdef0123456789abcdef01234567"
sha1, err := git.NewIDFromString(hexString)
sha1, err := git.IDFromString(hexString)
assert.NoError(t, err)
pushCommit := CommitToPushCommit(&git.Commit{
ID: sha1,
Expand All @@ -169,11 +169,12 @@ func TestListToPushCommits(t *testing.T) {
When: now,
}

hashType := git.ObjectFormatFromID(git.Sha1)
const hexString1 = "0123456789abcdef0123456789abcdef01234567"
hash1, err := git.NewIDFromString(hexString1)
hash1, err := hashType.NewIDFromString(hexString1)
assert.NoError(t, err)
const hexString2 = "fedcba9876543210fedcba9876543210fedcba98"
hash2, err := git.NewIDFromString(hexString2)
hash2, err := hashType.NewIDFromString(hexString2)
assert.NoError(t, err)

l := []*git.Commit{
Expand Down
6 changes: 4 additions & 2 deletions modules/repository/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
}
}

if err := git.InitRepository(ctx, tmpDir, false); err != nil {
// FIXME: fix the hash
if err := git.InitRepository(ctx, tmpDir, false, git.ObjectFormatFromID(git.Sha1)); err != nil {
return err
}

Expand Down Expand Up @@ -356,7 +357,8 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ
}
}

if err = CheckInitRepository(ctx, owner.Name, generateRepo.Name); err != nil {
// FIXME - fix the hash
if err = CheckInitRepository(ctx, owner.Name, generateRepo.Name, git.ObjectFormatFromID(git.Sha1)); err != nil {
return generateRepo, err
}

Expand Down
4 changes: 2 additions & 2 deletions modules/repository/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func InitRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
return nil
}

func CheckInitRepository(ctx context.Context, owner, name string) (err error) {
func CheckInitRepository(ctx context.Context, owner, name string, objectFormat git.ObjectFormat) (err error) {
// Somehow the directory could exist.
repoPath := repo_model.RepoPath(owner, name)
isExist, err := util.IsExist(repoPath)
Expand All @@ -204,7 +204,7 @@ func CheckInitRepository(ctx context.Context, owner, name string) (err error) {
}

// Init git bare new repository.
if err = git.InitRepository(ctx, repoPath, true); err != nil {
if err = git.InitRepository(ctx, repoPath, true, objectFormat); err != nil {
return fmt.Errorf("git.InitRepository: %w", err)
} else if err = CreateDelegateHooks(repoPath); err != nil {
return fmt.Errorf("createDelegateHooks: %w", err)
Expand Down
6 changes: 4 additions & 2 deletions modules/repository/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ type PushUpdateOptions struct {

// IsNewRef return true if it's a first-time push to a branch, tag or etc.
func (opts *PushUpdateOptions) IsNewRef() bool {
return opts.OldCommitID == git.EmptySHA
commitID, err := git.IDFromString(opts.OldCommitID)
return err == nil && commitID.IsZero()
}

// IsDelRef return true if it's a deletion to a branch or tag
func (opts *PushUpdateOptions) IsDelRef() bool {
return opts.NewCommitID == git.EmptySHA
commitID, err := git.IDFromString(opts.NewCommitID)
return err == nil && commitID.IsZero()
}

// IsUpdateRef return true if it's an update operation
Expand Down
4 changes: 2 additions & 2 deletions routers/api/v1/repo/notes.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func getNote(ctx *context.APIContext, identifier string) {
return
}

commitSHA, err := ctx.Repo.GitRepo.ConvertToSHA1(identifier)
commitID, err := ctx.Repo.GitRepo.ConvertToGitID(identifier)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(err)
Expand All @@ -77,7 +77,7 @@ func getNote(ctx *context.APIContext, identifier string) {
}

var note git.Note
if err := git.GetNote(ctx, ctx.Repo.GitRepo, commitSHA.String(), &note); err != nil {
if err := git.GetNote(ctx, ctx.Repo.GitRepo, commitID.String(), &note); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(identifier)
return
Expand Down
1 change: 1 addition & 0 deletions routers/api/v1/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
DefaultBranch: opt.DefaultBranch,
TrustModel: repo_model.ToTrustModel(opt.TrustModel),
IsTemplate: opt.Template,
ObjectFormat: git.ObjectFormatFromID(git.Sha1),
})
if err != nil {
if repo_model.IsErrRepoAlreadyExist(err) {
Expand Down
17 changes: 9 additions & 8 deletions routers/api/v1/utils/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,28 @@ func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (str
return "", "", nil
}

// ConvertToSHA1 returns a full-length SHA1 from a potential ID string
func ConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) (git.SHA1, error) {
if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) {
sha1, err := git.NewIDFromString(commitID)
// ConvertToObjectID returns a full-length SHA1 from a potential ID string
func ConvertToObjectID(ctx gocontext.Context, repo *context.Repository, commitID string) (git.ObjectID, error) {
objectFormat, _ := repo.GitRepo.GetObjectFormat()
if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) {
sha, err := objectFormat.NewIDFromString(commitID)
if err == nil {
return sha1, nil
return sha, nil
}
}

gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.Repository.RepoPath())
if err != nil {
return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
return objectFormat.Empty(), fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
}
defer closer.Close()

return gitRepo.ConvertToSHA1(commitID)
return gitRepo.ConvertToGitID(commitID)
}

// MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1
func MustConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) string {
sha, err := ConvertToSHA1(ctx, repo, commitID)
sha, err := ConvertToObjectID(ctx, repo, commitID)
if err != nil {
return commitID
}
Expand Down
3 changes: 1 addition & 2 deletions routers/private/hook_post_receive.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
}

// If we've pushed a branch (and not deleted it)
if newCommitID != git.EmptySHA && refFullName.IsBranch() {

if git.IsEmptyCommitID(newCommitID) && refFullName.IsBranch() {
// First ensure we have the repository loaded, we're allowed pulls requests and we can get the base repo
if repo == nil {
repo = loadRepository(ctx, ownerName, repoName)
Expand Down
7 changes: 4 additions & 3 deletions routers/private/hook_pre_receive.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r

repo := ctx.Repo.Repository
gitRepo := ctx.Repo.GitRepo
objectFormat, _ := gitRepo.GetObjectFormat()

if branchName == repo.DefaultBranch && newCommitID == git.EmptySHA {
if branchName == repo.DefaultBranch && newCommitID == objectFormat.Empty().String() {
log.Warn("Forbidden: Branch: %s is the default branch in %-v and cannot be deleted", branchName, repo)
ctx.JSON(http.StatusForbidden, private.Response{
UserMsg: fmt.Sprintf("branch %s is the default branch and cannot be deleted", branchName),
Expand Down Expand Up @@ -174,7 +175,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
// First of all we need to enforce absolutely:
//
// 1. Detect and prevent deletion of the branch
if newCommitID == git.EmptySHA {
if newCommitID == objectFormat.Empty().String() {
log.Warn("Forbidden: Branch: %s in %-v is protected from deletion", branchName, repo)
ctx.JSON(http.StatusForbidden, private.Response{
UserMsg: fmt.Sprintf("branch %s is protected from deletion", branchName),
Expand All @@ -183,7 +184,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
}

// 2. Disallow force pushes to protected branches
if git.EmptySHA != oldCommitID {
if oldCommitID != objectFormat.Empty().String() {
output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env})
if err != nil {
log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
Expand Down
8 changes: 5 additions & 3 deletions routers/private/hook_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
}()

var command *git.Command
if oldCommitID == git.EmptySHA {
objectFormat, _ := repo.GetObjectFormat()
if oldCommitID == objectFormat.Empty().String() {
// When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all":
// List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits
// So, it only lists the new commits received, doesn't list the commits already present in the receiving repository
Expand Down Expand Up @@ -82,7 +83,8 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}()
hash := git.MustIDFromString(sha)
objectFormat, _ := repo.GetObjectFormat()
commitID := objectFormat.MustIDFromString(sha)

return git.NewCommand(repo.Ctx, "cat-file", "commit").AddDynamicArguments(sha).
Run(&git.RunOpts{
Expand All @@ -91,7 +93,7 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
Stdout: stdoutWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
commit, err := git.CommitFromReader(repo, hash, stdoutReader)
commit, err := git.CommitFromReader(repo, commitID, stdoutReader)
if err != nil {
return err
}
Expand Down
7 changes: 5 additions & 2 deletions routers/private/hook_verification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ func TestVerifyCommits(t *testing.T) {
defer gitRepo.Close()
assert.NoError(t, err)

objectFormat, err := gitRepo.GetObjectFormat()
assert.NoError(t, err)

testCases := []struct {
base, head string
verified bool
}{
{"72920278f2f999e3005801e5d5b8ab8139d3641c", "d766f2917716d45be24bfa968b8409544941be32", true},
{git.EmptySHA, "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit
{objectFormat.Empty().String(), "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit
{"9779d17a04f1e2640583d35703c62460b2d86e0a", "72920278f2f999e3005801e5d5b8ab8139d3641c", false},
{git.EmptySHA, "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit
{objectFormat.Empty().String(), "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit
}

for _, tc := range testCases {
Expand Down
9 changes: 7 additions & 2 deletions routers/web/repo/blame.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ type blameResult struct {
}

func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
blameReader, err := git.CreateBlameReader(ctx, repoPath, commit, file, bypassBlameIgnore)
objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat()
if err != nil {
ctx.NotFound("CreateBlameReader", err)
return nil, err
}
blameReader, err := git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, bypassBlameIgnore)
if err != nil {
return nil, err
}
Expand All @@ -147,7 +152,7 @@ func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, fil
if len(r.Parts) == 0 && r.UsesIgnoreRevs {
// try again without ignored revs

blameReader, err = git.CreateBlameReader(ctx, repoPath, commit, file, true)
blameReader, err = git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, true)
if err != nil {
return nil, err
}
Expand Down
9 changes: 8 additions & 1 deletion routers/web/repo/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,18 @@ func RestoreBranchPost(ctx *context.Context) {
return
}

objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath())
if err != nil {
log.Error("RestoreBranch: CreateBranch: %w", err)
ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", deletedBranch.Name))
return
}

// Don't return error below this
if err := repo_service.PushUpdate(
&repo_module.PushUpdateOptions{
RefFullName: git.RefNameFromBranch(deletedBranch.Name),
OldCommitID: git.EmptySHA,
OldCommitID: objectFormat.Empty().String(),
NewCommitID: deletedBranch.CommitID,
PusherID: ctx.Doer.ID,
PusherName: ctx.Doer.Name,
Expand Down
2 changes: 1 addition & 1 deletion routers/web/repo/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func Diff(ctx *context.Context) {
}
return
}
if len(commitID) != git.SHAFullLength {
if len(commitID) != commit.ID.Type().FullLength() {
commitID = commit.ID.String()
}

Expand Down
3 changes: 2 additions & 1 deletion routers/web/repo/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,14 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch)
baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch)
baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat()
if !baseIsCommit && !baseIsBranch && !baseIsTag {
// Check if baseBranch is short sha commit hash
if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil {
ci.BaseBranch = baseCommit.ID.String()
ctx.Data["BaseBranch"] = ci.BaseBranch
baseIsCommit = true
} else if ci.BaseBranch == git.EmptySHA {
} else if ci.BaseBranch == objectFormat.Empty().String() {
if isSameRepo {
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch))
} else {
Expand Down
2 changes: 1 addition & 1 deletion routers/web/repo/githttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ func dummyInfoRefs(ctx *context.Context) {
}
}()

if err := git.InitRepository(ctx, tmpDir, true); err != nil {
if err := git.InitRepository(ctx, tmpDir, true, git.ObjectFormatFromID(git.Sha1)); err != nil {
log.Error("Failed to init bare repo for git-receive-pack cache: %v", err)
return
}
Expand Down
2 changes: 2 additions & 0 deletions routers/web/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ func Create(ctx *context.Context) {
ctx.Data["private"] = getRepoPrivate(ctx)
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
ctx.Data["default_branch"] = setting.Repository.DefaultBranch
ctx.Data["hash_type"] = "sha1"

ctxUser := checkContextUser(ctx, ctx.FormInt64("org"))
if ctx.Written() {
Expand Down Expand Up @@ -288,6 +289,7 @@ func CreatePost(ctx *context.Context) {
AutoInit: form.AutoInit,
IsTemplate: form.Template,
TrustModel: repo_model.ToTrustModel(form.TrustModel),
ObjectFormat: form.ObjectFormat,
})
if err == nil {
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
Expand Down
11 changes: 6 additions & 5 deletions routers/web/repo/setting/lfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,20 +388,21 @@ func LFSFileFind(ctx *context.Context) {
sha := ctx.FormString("sha")
ctx.Data["Title"] = oid
ctx.Data["PageIsSettingsLFS"] = true
var hash git.SHA1
objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat()
var objectID git.ObjectID
if len(sha) == 0 {
pointer := lfs.Pointer{Oid: oid, Size: size}
hash = git.ComputeBlobHash([]byte(pointer.StringContent()))
sha = hash.String()
objectID = git.ComputeBlobHash(objectFormat, []byte(pointer.StringContent()))
sha = objectID.String()
} else {
hash = git.MustIDFromString(sha)
objectID = objectFormat.MustIDFromString(sha)
}
ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
ctx.Data["Oid"] = oid
ctx.Data["Size"] = size
ctx.Data["SHA"] = sha

results, err := pipeline.FindLFSFile(ctx.Repo.GitRepo, hash)
results, err := pipeline.FindLFSFile(ctx.Repo.GitRepo, objectID)
if err != nil && err != io.EOF {
log.Error("Failure in FindLFSFile: %v", err)
ctx.ServerError("LFSFind: FindLFSFile.", err)
Expand Down
8 changes: 7 additions & 1 deletion routers/web/repo/setting/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,8 +655,14 @@ func TestWebhook(ctx *context.Context) {
commit := ctx.Repo.Commit
if commit == nil {
ghost := user_model.NewGhostUser()
objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath())
if err != nil {
ctx.Flash.Error("GetObjectFormatOfRepo: " + err.Error())
ctx.Status(http.StatusInternalServerError)
return
}
commit = &git.Commit{
ID: git.MustIDFromString(git.EmptySHA),
ID: objectFormat.NewEmptyID(),
Author: ghost.NewGitSig(),
Committer: ghost.NewGitSig(),
CommitMessage: "This is a fake commit",
Expand Down
7 changes: 6 additions & 1 deletion services/actions/commit_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
user_model "code.gitea.io/gitea/models/user"
git "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"
Expand Down Expand Up @@ -114,9 +115,13 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
}

creator := user_model.NewActionsUser()
commitID, err := git.IDFromString(sha)
if err != nil {
return fmt.Errorf("HashTypeInterfaceFromHashString: %w", err)
}
if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
Repo: repo,
SHA: sha,
SHA: commitID,
Creator: creator,
CommitStatus: &git_model.CommitStatus{
SHA: sha,
Expand Down
6 changes: 4 additions & 2 deletions services/agit/agit.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.

topicBranch = opts.GitPushOptions["topic"]
_, forcePush = opts.GitPushOptions["force-push"]
objectFormat, _ := gitRepo.GetObjectFormat()

for i := range opts.OldCommitIDs {
if opts.NewCommitIDs[i] == git.EmptySHA {
if opts.NewCommitIDs[i] == objectFormat.Empty().String() {
results = append(results, private.HookProcReceiveRefResult{
OriginalRef: opts.RefFullNames[i],
OldOID: opts.OldCommitIDs[i],
Expand Down Expand Up @@ -148,10 +149,11 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.

log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)

objectFormat, _ := gitRepo.GetObjectFormat()
results = append(results, private.HookProcReceiveRefResult{
Ref: pr.GetGitRefName(),
OriginalRef: opts.RefFullNames[i],
OldOID: git.EmptySHA,
OldOID: objectFormat.Empty().String(),
NewOID: opts.NewCommitIDs[i],
})
continue
Expand Down
10 changes: 5 additions & 5 deletions services/convert/git_commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import (
func TestToCommitMeta(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
sha1, _ := git.NewIDFromString("0000000000000000000000000000000000000000")
sha1 := git.ObjectFormatFromID(git.Sha1)
signature := &git.Signature{Name: "Test Signature", Email: "test@email.com", When: time.Unix(0, 0)}
tag := &git.Tag{
Name: "Test Tag",
ID: sha1,
Object: sha1,
ID: sha1.Empty(),
Object: sha1.Empty(),
Type: "Test Type",
Tagger: signature,
Message: "Test Message",
Expand All @@ -34,8 +34,8 @@ func TestToCommitMeta(t *testing.T) {

assert.NotNil(t, commitMeta)
assert.EqualValues(t, &api.CommitMeta{
SHA: "0000000000000000000000000000000000000000",
URL: util.URLJoin(headRepo.APIURL(), "git/commits", "0000000000000000000000000000000000000000"),
SHA: sha1.Empty().String(),
URL: util.URLJoin(headRepo.APIURL(), "git/commits", sha1.Empty().String()),
Created: time.Unix(0, 0),
}, commitMeta)
}
2 changes: 2 additions & 0 deletions services/forms/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
project_model "code.gitea.io/gitea/models/project"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
Expand Down Expand Up @@ -53,6 +54,7 @@ type CreateRepoForm struct {
TrustModel string

ForkSingleBranch string
ObjectFormat git.ObjectFormat
}

// Validate validates the fields
Expand Down
24 changes: 16 additions & 8 deletions services/gitdiff/gitdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -1115,10 +1115,15 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
}

cmdDiff := git.NewCommand(gitRepo.Ctx)
if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA) && commit.ParentCount() == 0 {
objectFormat, err := gitRepo.GetObjectFormat()
if err != nil {
return nil, err
}

if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.Empty().String()) && commit.ParentCount() == 0 {
cmdDiff.AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
AddArguments(opts.WhitespaceBehavior...).
AddArguments("4b825dc642cb6eb9a060e54bf8d69288fbee4904"). // append empty tree ref
AddDynamicArguments(objectFormat.EmptyTree().String()).
AddDynamicArguments(opts.AfterCommitID)
} else {
actualBeforeCommitID := opts.BeforeCommitID
Expand Down Expand Up @@ -1224,8 +1229,8 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
}

diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA {
diffPaths = []string{git.EmptyTreeSHA, opts.AfterCommitID}
if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.Empty().String() {
diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID}
}
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
if err != nil && strings.Contains(err.Error(), "no merge base") {
Expand Down Expand Up @@ -1256,12 +1261,15 @@ func GetPullDiffStats(gitRepo *git.Repository, opts *DiffOptions) (*PullDiffStat
separator = ".."
}

diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA {
diffPaths = []string{git.EmptyTreeSHA, opts.AfterCommitID}
objectFormat, err := gitRepo.GetObjectFormat()
if err != nil {
return nil, err
}

var err error
diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.Empty().String() {
diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID}
}

_, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
if err != nil && strings.Contains(err.Error(), "no merge base") {
Expand Down
8 changes: 5 additions & 3 deletions services/migrations/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,18 @@ func CheckAndEnsureSafePR(pr *base.PullRequest, commonCloneBaseURL string, g bas
}

// SECURITY: SHAs Must be a SHA
if pr.MergeCommitSHA != "" && !git.IsValidSHAPattern(pr.MergeCommitSHA) {
// FIXME: hash only a SHA1
CommitType := git.ObjectFormatFromID(git.Sha1)
if pr.MergeCommitSHA != "" && !CommitType.IsValid(pr.MergeCommitSHA) {
WarnAndNotice("PR #%d in %s has invalid MergeCommitSHA: %s", pr.Number, g, pr.MergeCommitSHA)
pr.MergeCommitSHA = ""
}
if pr.Head.SHA != "" && !git.IsValidSHAPattern(pr.Head.SHA) {
if pr.Head.SHA != "" && !CommitType.IsValid(pr.Head.SHA) {
WarnAndNotice("PR #%d in %s has invalid HeadSHA: %s", pr.Number, g, pr.Head.SHA)
pr.Head.SHA = ""
valid = false
}
if pr.Base.SHA != "" && !git.IsValidSHAPattern(pr.Base.SHA) {
if pr.Base.SHA != "" && !CommitType.IsValid(pr.Base.SHA) {
WarnAndNotice("PR #%d in %s has invalid BaseSHA: %s", pr.Number, g, pr.Base.SHA)
pr.Base.SHA = ""
valid = false
Expand Down
3 changes: 2 additions & 1 deletion services/migrations/gitea_uploader.go
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,8 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
comment.UpdatedAt = comment.CreatedAt
}

if !git.IsValidSHAPattern(comment.CommitID) {
objectFormat, _ := g.gitRepo.GetObjectFormat()
if !objectFormat.IsValid(comment.CommitID) {
log.Warn("Invalid comment CommitID[%s] on comment[%d] in PR #%d of %s/%s replaced with %s", comment.CommitID, pr.Index, g.repoOwner, g.repoName, headCommitID)
comment.CommitID = headCommitID
}
Expand Down
2 changes: 1 addition & 1 deletion services/migrations/gitea_uploader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
//
fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
baseRef := "master"
assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false))
assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false, fromRepo.ObjectFormat))
err := git.NewCommand(git.DefaultContext, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(&git.RunOpts{Dir: fromRepo.RepoPath()})
assert.NoError(t, err)
assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", fromRepo.RepoPath())), 0o644))
Expand Down
6 changes: 5 additions & 1 deletion services/mirror/mirror_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,9 +478,13 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err)
continue
}
objectFormat, err := git.GetObjectFormatOfRepo(ctx, m.Repo.RepoPath())
if err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to GetHashTypeOfRepo: %v", m.Repo, err)
}
notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{
RefFullName: result.refName,
OldCommitID: git.EmptySHA,
OldCommitID: objectFormat.Empty().String(),
NewCommitID: commitID,
}, repo_module.NewPushCommits())
notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName, commitID)
Expand Down
2 changes: 1 addition & 1 deletion services/packages/cargo/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *re
if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
return err
}
if err := t.Init(); err != nil {
if err := t.Init(repo.ObjectFormat); err != nil {
return err
}
} else {
Expand Down
19 changes: 12 additions & 7 deletions services/pull/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,24 +215,29 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
return nil, fmt.Errorf("GetFullCommitID(%s) in %s: %w", prHeadRef, pr.BaseRepo.FullName(), err)
}

gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
if err != nil {
return nil, fmt.Errorf("%-v OpenRepository: %w", pr.BaseRepo, err)
}
defer gitRepo.Close()

objectFormat, err := gitRepo.GetObjectFormat()
if err != nil {
return nil, fmt.Errorf("%-v GetObjectFormat: %w", pr.BaseRepo, err)
}

// Get the commit from BaseBranch where the pull request got merged
mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse").
AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch).
RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
if err != nil {
return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err)
} else if len(mergeCommit) < git.SHAFullLength {
} else if len(mergeCommit) < objectFormat.FullLength() {
// PR was maybe fast-forwarded, so just use last commit of PR
mergeCommit = prHeadCommitID
}
mergeCommit = strings.TrimSpace(mergeCommit)

gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
if err != nil {
return nil, fmt.Errorf("%-v OpenRepository: %w", pr.BaseRepo, err)
}
defer gitRepo.Close()

commit, err := gitRepo.GetCommit(mergeCommit)
if err != nil {
return nil, fmt.Errorf("GetMergeCommit[%s]: %w", mergeCommit, err)
Expand Down
3 changes: 2 additions & 1 deletion services/pull/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,8 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged}
}

if len(commitID) < git.SHAFullLength {
objectFormat, _ := baseGitRepo.GetObjectFormat()
if len(commitID) != objectFormat.FullLength() {
return fmt.Errorf("Wrong commit ID")
}

Expand Down
Loading