diff --git a/cmd/hook.go b/cmd/hook.go
index f38fd8831b8..624569cdfa7 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -376,7 +376,9 @@ Gitea or set your environment appropriately.`, "")
 		oldCommitIDs[count] = string(fields[0])
 		newCommitIDs[count] = string(fields[1])
 		refFullNames[count] = git.RefName(fields[2])
-		if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
+
+		commitID, _ := git.IDFromString(newCommitIDs[count])
+		if refFullNames[count] == git.BranchPrefix+"master" && !commitID.IsZero() && count == total {
 			masterPushed = true
 		}
 		count++
@@ -669,7 +671,8 @@ Gitea or set your environment appropriately.`, "")
 		if err != nil {
 			return err
 		}
-		if rs.OldOID != git.EmptySHA {
+		commitID, _ := git.IDFromString(rs.OldOID)
+		if !commitID.IsZero() {
 			err = writeDataPktLine(ctx, os.Stdout, []byte("option old-oid "+rs.OldOID))
 			if err != nil {
 				return err
diff --git a/models/git/branch_test.go b/models/git/branch_test.go
index ce4cbd56a12..adcf9fd305a 100644
--- a/models/git/branch_test.go
+++ b/models/git/branch_test.go
@@ -30,7 +30,7 @@ func TestAddDeletedBranch(t *testing.T) {
 	assert.True(t, secondBranch.IsDeleted)
 
 	commit := &git.Commit{
-		ID:            git.MustIDFromString(secondBranch.CommitID),
+		ID:            repo.ObjectFormat.MustIDFromString(secondBranch.CommitID),
 		CommitMessage: secondBranch.CommitMessage,
 		Committer: &git.Signature{
 			When: secondBranch.CommitTime.AsLocalTime(),
diff --git a/models/git/commit_status.go b/models/git/commit_status.go
index 097ce01c144..a22fd230436 100644
--- a/models/git/commit_status.go
+++ b/models/git/commit_status.go
@@ -114,7 +114,8 @@ WHEN NOT MATCHED
 
 // GetNextCommitStatusIndex retried 3 times to generate a resource index
 func GetNextCommitStatusIndex(ctx context.Context, repoID int64, sha string) (int64, error) {
-	if !git.IsValidSHAPattern(sha) {
+	_, err := git.IDFromString(sha)
+	if err != nil {
 		return 0, git.ErrInvalidSHA{SHA: sha}
 	}
 
@@ -425,7 +426,7 @@ func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, befor
 type NewCommitStatusOptions struct {
 	Repo         *repo_model.Repository
 	Creator      *user_model.User
-	SHA          string
+	SHA          git.ObjectID
 	CommitStatus *CommitStatus
 }
 
@@ -440,10 +441,6 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
 		return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
 	}
 
-	if _, err := git.NewIDFromString(opts.SHA); err != nil {
-		return fmt.Errorf("NewCommitStatus[%s, %s]: invalid sha: %w", repoPath, opts.SHA, err)
-	}
-
 	ctx, committer, err := db.TxContext(ctx)
 	if err != nil {
 		return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", opts.Repo.ID, opts.Creator.ID, opts.SHA, err)
@@ -451,7 +448,7 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
 	defer committer.Close()
 
 	// Get the next Status Index
-	idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA)
+	idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
 	if err != nil {
 		return fmt.Errorf("generate commit status index failed: %w", err)
 	}
@@ -459,7 +456,7 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
 	opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
 	opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
 	opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
-	opts.CommitStatus.SHA = opts.SHA
+	opts.CommitStatus.SHA = opts.SHA.String()
 	opts.CommitStatus.CreatorID = opts.Creator.ID
 	opts.CommitStatus.RepoID = opts.Repo.ID
 	opts.CommitStatus.Index = idx
diff --git a/models/repo/repo.go b/models/repo/repo.go
index db3709f1e80..59f68df996a 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup"
 	"code.gitea.io/gitea/modules/setting"
@@ -179,6 +180,7 @@ type Repository struct {
 	IsFsckEnabled                   bool               `xorm:"NOT NULL DEFAULT true"`
 	CloseIssuesViaCommitInAnyBranch bool               `xorm:"NOT NULL DEFAULT false"`
 	Topics                          []string           `xorm:"TEXT JSON"`
+	ObjectFormat                    git.ObjectFormat   `xorm:"-"`
 
 	TrustModel TrustModelType
 
@@ -274,6 +276,8 @@ func (repo *Repository) AfterLoad() {
 	repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
 	repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects
 	repo.NumOpenActionRuns = repo.NumActionRuns - repo.NumClosedActionRuns
+
+	repo.ObjectFormat = git.ObjectFormatFromID(git.Sha1)
 }
 
 // LoadAttributes loads attributes of the repository.
@@ -313,7 +317,7 @@ func (repo *Repository) HTMLURL() string {
 // CommitLink make link to by commit full ID
 // note: won't check whether it's an right id
 func (repo *Repository) CommitLink(commitID string) (result string) {
-	if commitID == "" || commitID == "0000000000000000000000000000000000000000" {
+	if git.IsEmptyCommitID(commitID) {
 		result = ""
 	} else {
 		result = repo.Link() + "/commit/" + url.PathEscape(commitID)
diff --git a/modules/context/api.go b/modules/context/api.go
index ba35adf8310..f41228ad767 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -308,6 +308,12 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 			return
 		}
 
+		objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat()
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+			return
+		}
+
 		if ref := ctx.FormTrim("ref"); len(ref) > 0 {
 			commit, err := ctx.Repo.GitRepo.GetCommit(ref)
 			if err != nil {
@@ -325,7 +331,6 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 			return
 		}
 
-		var err error
 		refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
 
 		if ctx.Repo.GitRepo.IsBranchExist(refName) {
@@ -342,7 +347,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 				return
 			}
 			ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
-		} else if len(refName) == git.SHAFullLength {
+		} else if len(refName) == objectFormat.FullLength() {
 			ctx.Repo.CommitID = refName
 			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
 			if err != nil {
diff --git a/modules/context/repo.go b/modules/context/repo.go
index a18dc873b64..882a406731f 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -825,7 +825,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 		}
 		// For legacy and API support only full commit sha
 		parts := strings.Split(path, "/")
-		if len(parts) > 0 && len(parts[0]) == git.SHAFullLength {
+		objectFormat, _ := repo.GitRepo.GetObjectFormat()
+
+		if len(parts) > 0 && len(parts[0]) == objectFormat.FullLength() {
 			repo.TreePath = strings.Join(parts[1:], "/")
 			return parts[0]
 		}
@@ -869,7 +871,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 		return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
 	case RepoRefCommit:
 		parts := strings.Split(path, "/")
-		if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= git.SHAFullLength {
+		objectFormat, _ := repo.GitRepo.GetObjectFormat()
+
+		if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= objectFormat.FullLength() {
 			repo.TreePath = strings.Join(parts[1:], "/")
 			return parts[0]
 		}
@@ -929,6 +933,12 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 			}
 		}
 
+		objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat()
+		if err != nil {
+			log.Error("Cannot determine objectFormat for repository: %w", err)
+			ctx.Repo.Repository.MarkAsBrokenEmpty()
+		}
+
 		// Get default branch.
 		if len(ctx.Params("*")) == 0 {
 			refName = ctx.Repo.Repository.DefaultBranch
@@ -995,7 +1005,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 					return cancel
 				}
 				ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
-			} else if len(refName) >= 7 && len(refName) <= git.SHAFullLength {
+			} else if len(refName) >= 7 && len(refName) <= objectFormat.FullLength() {
 				ctx.Repo.IsViewCommit = true
 				ctx.Repo.CommitID = refName
 
@@ -1005,7 +1015,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 					return cancel
 				}
 				// If short commit ID add canonical link header
-				if len(refName) < git.SHAFullLength {
+				if len(refName) < objectFormat.FullLength() {
 					ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
 						util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
 				}
diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go
index 7a44e6295c4..53a9393d5fc 100644
--- a/modules/git/batch_reader.go
+++ b/modules/git/batch_reader.go
@@ -148,7 +148,7 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
 // ReadBatchLine reads the header line from cat-file --batch
 // We expect:
 // <sha> SP <type> SP <size> LF
-// sha is a 40byte not 20byte here
+// sha is a hex encoded here
 func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) {
 	typ, err = rd.ReadString('\n')
 	if err != nil {
@@ -251,20 +251,19 @@ headerLoop:
 }
 
 // git tree files are a list:
-// <mode-in-ascii> SP <fname> NUL <20-byte SHA>
+// <mode-in-ascii> SP <fname> NUL <binary Hash>
 //
 // Unfortunately this 20-byte notation is somewhat in conflict to all other git tools
-// Therefore we need some method to convert these 20-byte SHAs to a 40-byte SHA
+// Therefore we need some method to convert these binary hashes to hex hashes
 
-// constant hextable to help quickly convert between 20byte and 40byte hashes
+// constant hextable to help quickly convert between binary and hex representation
 const hextable = "0123456789abcdef"
 
-// To40ByteSHA converts a 20-byte SHA into a 40-byte sha. Input and output can be the
-// same 40 byte slice to support in place conversion without allocations.
+// BinToHexHeash converts a binary Hash into a hex encoded one. Input and output can be the
+// same byte slice to support in place conversion without allocations.
 // This is at least 100x quicker that hex.EncodeToString
-// NB This requires that out is a 40-byte slice
-func To40ByteSHA(sha, out []byte) []byte {
-	for i := 19; i >= 0; i-- {
+func BinToHex(objectFormat ObjectFormat, sha, out []byte) []byte {
+	for i := objectFormat.FullLength()/2 - 1; i >= 0; i-- {
 		v := sha[i]
 		vhi, vlo := v>>4, v&0x0f
 		shi, slo := hextable[vhi], hextable[vlo]
@@ -278,10 +277,10 @@ func To40ByteSHA(sha, out []byte) []byte {
 // It is recommended therefore to pass in an fnameBuf large enough to avoid almost all allocations
 //
 // Each line is composed of:
-// <mode-in-ascii-dropping-initial-zeros> SP <fname> NUL <20-byte SHA>
+// <mode-in-ascii-dropping-initial-zeros> SP <fname> NUL <binary HASH>
 //
-// We don't attempt to convert the 20-byte SHA to 40-byte SHA to save a lot of time
-func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fname, sha []byte, n int, err error) {
+// We don't attempt to convert the raw HASH to save a lot of time
+func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fname, sha []byte, n int, err error) {
 	var readBytes []byte
 
 	// Read the Mode & fname
@@ -324,11 +323,12 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
 	fnameBuf = fnameBuf[:len(fnameBuf)-1]
 	fname = fnameBuf
 
-	// Deal with the 20-byte SHA
+	// Deal with the binary hash
 	idx = 0
-	for idx < 20 {
+	len := objectFormat.FullLength() / 2
+	for idx < len {
 		var read int
-		read, err = rd.Read(shaBuf[idx:20])
+		read, err = rd.Read(shaBuf[idx:len])
 		n += read
 		if err != nil {
 			return mode, fname, sha, n, err
diff --git a/modules/git/blame.go b/modules/git/blame.go
index 93c7f184fa2..64095a218a3 100644
--- a/modules/git/blame.go
+++ b/modules/git/blame.go
@@ -10,8 +10,6 @@ import (
 	"fmt"
 	"io"
 	"os"
-	"regexp"
-	"strings"
 
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/util"
@@ -33,14 +31,13 @@ type BlameReader struct {
 	done           chan error
 	lastSha        *string
 	ignoreRevsFile *string
+	objectFormat   ObjectFormat
 }
 
 func (r *BlameReader) UsesIgnoreRevs() bool {
 	return r.ignoreRevsFile != nil
 }
 
-var shaLineRegex = regexp.MustCompile("^([a-z0-9]{40})")
-
 // NextPart returns next part of blame (sequential code lines with the same commit)
 func (r *BlameReader) NextPart() (*BlamePart, error) {
 	var blamePart *BlamePart
@@ -52,6 +49,7 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
 		}
 	}
 
+	const previousHeader = "previous "
 	var lineBytes []byte
 	var isPrefix bool
 	var err error
@@ -67,21 +65,22 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
 			continue
 		}
 
-		line := string(lineBytes)
-
-		lines := shaLineRegex.FindStringSubmatch(line)
-		if lines != nil {
-			sha1 := lines[1]
+		var objectID string
+		objectFormatLength := r.objectFormat.FullLength()
 
+		if len(lineBytes) > objectFormatLength && lineBytes[objectFormatLength] == ' ' && r.objectFormat.IsValid(string(lineBytes[0:objectFormatLength])) {
+			objectID = string(lineBytes[0:objectFormatLength])
+		}
+		if len(objectID) > 0 {
 			if blamePart == nil {
 				blamePart = &BlamePart{
-					Sha:   sha1,
+					Sha:   objectID,
 					Lines: make([]string, 0),
 				}
 			}
 
-			if blamePart.Sha != sha1 {
-				r.lastSha = &sha1
+			if blamePart.Sha != objectID {
+				r.lastSha = &objectID
 				// need to munch to end of line...
 				for isPrefix {
 					_, isPrefix, err = r.bufferedReader.ReadLine()
@@ -91,12 +90,13 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
 				}
 				return blamePart, nil
 			}
-		} else if line[0] == '\t' {
-			blamePart.Lines = append(blamePart.Lines, line[1:])
-		} else if strings.HasPrefix(line, "previous ") {
-			parts := strings.SplitN(line[len("previous "):], " ", 2)
-			blamePart.PreviousSha = parts[0]
-			blamePart.PreviousPath = parts[1]
+		} else if lineBytes[0] == '\t' {
+			blamePart.Lines = append(blamePart.Lines, string(lineBytes[1:]))
+		} else if bytes.HasPrefix(lineBytes, []byte(previousHeader)) {
+			offset := len(previousHeader) // already includes a space
+			blamePart.PreviousSha = string(lineBytes[offset : offset+objectFormatLength])
+			offset += objectFormatLength + 1 // +1 for space
+			blamePart.PreviousPath = string(lineBytes[offset:])
 		}
 
 		// need to munch to end of line...
@@ -126,7 +126,7 @@ func (r *BlameReader) Close() error {
 }
 
 // CreateBlameReader creates reader for given repository, commit and file
-func CreateBlameReader(ctx context.Context, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
+func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
 	var ignoreRevsFile *string
 	if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore {
 		ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
@@ -175,6 +175,7 @@ func CreateBlameReader(ctx context.Context, repoPath string, commit *Commit, fil
 		bufferedReader: bufferedReader,
 		done:           done,
 		ignoreRevsFile: ignoreRevsFile,
+		objectFormat:   objectFormat,
 	}, nil
 }
 
diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go
index 040f4e822da..0afc6d2a1f0 100644
--- a/modules/git/blame_test.go
+++ b/modules/git/blame_test.go
@@ -39,7 +39,7 @@ func TestReadingBlameOutput(t *testing.T) {
 		}
 
 		for _, bypass := range []bool{false, true} {
-			blameReader, err := CreateBlameReader(ctx, "./tests/repos/repo5_pulls", commit, "README.md", bypass)
+			blameReader, err := CreateBlameReader(ctx, &Sha1ObjectFormat{}, "./tests/repos/repo5_pulls", commit, "README.md", bypass)
 			assert.NoError(t, err)
 			assert.NotNil(t, blameReader)
 			defer blameReader.Close()
@@ -122,7 +122,7 @@ func TestReadingBlameOutput(t *testing.T) {
 			commit, err := repo.GetCommit(c.CommitID)
 			assert.NoError(t, err)
 
-			blameReader, err := CreateBlameReader(ctx, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass)
+			blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass)
 			assert.NoError(t, err)
 			assert.NotNil(t, blameReader)
 			defer blameReader.Close()
diff --git a/modules/git/blob_gogit.go b/modules/git/blob_gogit.go
index aa206409d0b..8c79c067c17 100644
--- a/modules/git/blob_gogit.go
+++ b/modules/git/blob_gogit.go
@@ -14,7 +14,7 @@ import (
 
 // Blob represents a Git object.
 type Blob struct {
-	ID SHA1
+	ID ObjectID
 
 	gogitEncodedObj plumbing.EncodedObject
 	name            string
diff --git a/modules/git/blob_nogogit.go b/modules/git/blob_nogogit.go
index 511332eb506..6e8a48b1db0 100644
--- a/modules/git/blob_nogogit.go
+++ b/modules/git/blob_nogogit.go
@@ -16,7 +16,7 @@ import (
 
 // Blob represents a Git object.
 type Blob struct {
-	ID SHA1
+	ID ObjectID
 
 	gotSize bool
 	size    int64
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 4ff8f6148f7..a8b6c0e8f79 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -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
 }
 
@@ -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
 }
@@ -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
@@ -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
diff --git a/modules/git/commit_convert_gogit.go b/modules/git/commit_convert_gogit.go
index 669f1b15d53..819ea0d1dba 100644
--- a/modules/git/commit_convert_gogit.go
+++ b/modules/git/commit_convert_gogit.go
@@ -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),
 	}
 }
diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go
index c61d27993cd..31ffc9aec1c 100644
--- a/modules/git/commit_info_gogit.go
+++ b/modules/git/commit_info_gogit.go
@@ -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
 	}
diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go
index e469d2cab63..8cf8200c3f7 100644
--- a/modules/git/commit_info_nogogit.go
+++ b/modules/git/commit_info_nogogit.go
@@ -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
 		}
diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go
index 23607c43abe..08a529a1321 100644
--- a/modules/git/commit_reader.go
+++ b/modules/git/commit_reader.go
@@ -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{},
 	}
@@ -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{}
diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go
index ac586fdf09d..dec67f66286 100644
--- a/modules/git/commit_test.go
+++ b/modules/git/commit_test.go
@@ -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)
@@ -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)
diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go
index 20bc7960855..55585ac4ac9 100644
--- a/modules/git/last_commit_cache.go
+++ b/modules/git/last_commit_cache.go
@@ -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
 	}
diff --git a/modules/git/last_commit_cache_gogit.go b/modules/git/last_commit_cache_gogit.go
index 90e609fc81c..3afc2130942 100644
--- a/modules/git/last_commit_cache_gogit.go
+++ b/modules/git/last_commit_cache_gogit.go
@@ -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"
 )
 
@@ -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
 	}
diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go
index 7519e32b900..26a0d28098a 100644
--- a/modules/git/log_name_status.go
+++ b/modules/git/log_name_status.go
@@ -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
diff --git a/modules/git/notes_gogit.go b/modules/git/notes_gogit.go
index c2297d89703..f802443b00e 100644
--- a/modules/git/notes_gogit.go
+++ b/modules/git/notes_gogit.go
@@ -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"
 )
 
@@ -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
 	}
diff --git a/modules/git/object_format.go b/modules/git/object_format.go
new file mode 100644
index 00000000000..7f5d09170c6
--- /dev/null
+++ b/modules/git/object_format.go
@@ -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
+)
+
+// 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)
+}
diff --git a/modules/git/object_id.go b/modules/git/object_id.go
new file mode 100644
index 00000000000..3cba6d4f724
--- /dev/null
+++ b/modules/git/object_id.go
@@ -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()
+}
+
+// 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)
+}
diff --git a/modules/git/object_id_gogit.go b/modules/git/object_id_gogit.go
new file mode 100644
index 00000000000..50917f0552d
--- /dev/null
+++ b/modules/git/object_id_gogit.go
@@ -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
+}
diff --git a/modules/git/object_id_test.go b/modules/git/object_id_test.go
new file mode 100644
index 00000000000..c78a2157553
--- /dev/null
+++ b/modules/git/object_id_test.go
@@ -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"))
+}
diff --git a/modules/git/parse_gogit.go b/modules/git/parse_gogit.go
index 226ef5df731..6c22ea8da7b 100644
--- a/modules/git/parse_gogit.go
+++ b/modules/git/parse_gogit.go
@@ -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)
 }
 
@@ -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')
@@ -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)
diff --git a/modules/git/parse_gogit_test.go b/modules/git/parse_gogit_test.go
index f6e32401e66..7ba50cbff90 100644
--- a/modules/git/parse_gogit_test.go
+++ b/modules/git/parse_gogit_test.go
@@ -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"
@@ -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,
 					},
@@ -42,9 +44,9 @@ 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,
 					},
@@ -52,10 +54,10 @@ func TestParseTreeEntries(t *testing.T) {
 					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,
 					},
@@ -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)
 	}
 }
diff --git a/modules/git/parse_nogogit.go b/modules/git/parse_nogogit.go
index 8b94c69200c..e35704eb34e 100644
--- a/modules/git/parse_nogogit.go
+++ b/modules/git/parse_nogogit.go
@@ -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); {
@@ -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)
 		}
@@ -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
@@ -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)
 	}
diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go
index 23fddb014c1..0b78c081cd6 100644
--- a/modules/git/parse_nogogit_test.go
+++ b/modules/git/parse_nogogit_test.go
@@ -12,6 +12,8 @@ import (
 )
 
 func TestParseTreeEntriesLong(t *testing.T) {
+	objectFormat := ObjectFormatFromID(Sha1)
+
 	testCases := []struct {
 		Input    string
 		Expected []*TreeEntry
@@ -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,
@@ -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 {
@@ -64,6 +66,8 @@ func TestParseTreeEntriesLong(t *testing.T) {
 }
 
 func TestParseTreeEntriesShort(t *testing.T) {
+	objectFormat := ObjectFormatFromID(Sha1)
+
 	testCases := []struct {
 		Input    string
 		Expected []*TreeEntry
@@ -74,12 +78,12 @@ 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,
 				},
@@ -87,7 +91,7 @@ func TestParseTreeEntriesShort(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 {
@@ -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)
 }
diff --git a/modules/git/pipeline/lfs.go b/modules/git/pipeline/lfs.go
index ee0505f29f9..6dfca24f29a 100644
--- a/modules/git/pipeline/lfs.go
+++ b/modules/git/pipeline/lfs.go
@@ -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"
 )
 
@@ -26,7 +27,7 @@ type LFSResult struct {
 	SHA            string
 	Summary        string
 	When           time.Time
-	ParentHashes   []git.SHA1
+	ParentHashes   []git.ObjectID
 	BranchName     string
 	FullCommitName string
 }
@@ -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)
 
@@ -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
 			}
diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go
index 49390f7c00c..89cbf740ffa 100644
--- a/modules/git/pipeline/lfs_nogogit.go
+++ b/modules/git/pipeline/lfs_nogogit.go
@@ -24,7 +24,7 @@ type LFSResult struct {
 	SHA            string
 	Summary        string
 	When           time.Time
-	ParentHashes   []git.SHA1
+	ParentIDs      []git.ObjectID
 	BranchName     string
 	FullCommitName string
 }
@@ -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)
 
@@ -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
@@ -115,7 +115,11 @@ 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
 				}
@@ -123,32 +127,31 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
 					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)+"/")
 					}
 				}
@@ -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
 			}
 		}
diff --git a/modules/git/ref.go b/modules/git/ref.go
index ad251515e7d..b96b4ababb4 100644
--- a/modules/git/ref.go
+++ b/modules/git/ref.go
@@ -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
 }
 
@@ -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:
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 32f0e7007eb..871d267a5b8 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -7,6 +7,7 @@ package git
 import (
 	"bytes"
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"net/url"
@@ -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")
 	}
diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go
index ef59ead9003..d0b8e793684 100644
--- a/modules/git/repo_base_gogit.go
+++ b/modules/git/repo_base_gogit.go
@@ -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"
 )
@@ -32,6 +33,7 @@ type Repository struct {
 
 	Ctx             context.Context
 	LastCommitCache *LastCommitCache
+	objectFormat    ObjectFormat
 }
 
 // openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
@@ -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
 }
 
diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go
index 414e4eb1a83..a783366cc1f 100644
--- a/modules/git/repo_base_nogogit.go
+++ b/modules/git/repo_base_nogogit.go
@@ -33,6 +33,8 @@ type Repository struct {
 
 	Ctx             context.Context
 	LastCommitCache *LastCommitCache
+
+	objectFormat ObjectFormat
 }
 
 // openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
@@ -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
 }
 
diff --git a/modules/git/repo_blob.go b/modules/git/repo_blob.go
index 698b6c7074a..b5447b2bb13 100644
--- a/modules/git/repo_blob.go
+++ b/modules/git/repo_blob.go
@@ -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
 	}
diff --git a/modules/git/repo_blob_gogit.go b/modules/git/repo_blob_gogit.go
index 7f0892f6f5e..66c8c2775ce 100644
--- a/modules/git/repo_blob_gogit.go
+++ b/modules/git/repo_blob_gogit.go
@@ -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(), ""}
 	}
diff --git a/modules/git/repo_blob_nogogit.go b/modules/git/repo_blob_nogogit.go
index 184a2bc9dd0..04b0fb00ff7 100644
--- a/modules/git/repo_blob_nogogit.go
+++ b/modules/git/repo_blob_nogogit.go
@@ -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(), ""}
 	}
diff --git a/modules/git/repo_blob_test.go b/modules/git/repo_blob_test.go
index 026c73a2833..e1225739541 100644
--- a/modules/git/repo_blob_test.go
+++ b/modules/git/repo_blob_test.go
@@ -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)
diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go
index 6b06fef656e..58bbcf9303f 100644
--- a/modules/git/repo_commit.go
+++ b/modules/git/repo_commit.go
@@ -6,8 +6,6 @@ package git
 
 import (
 	"bytes"
-	"encoding/hex"
-	"fmt"
 	"io"
 	"strconv"
 	"strings"
@@ -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
 	}
@@ -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
@@ -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
 	}
@@ -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).
@@ -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
@@ -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
@@ -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
 		}
@@ -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)
@@ -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)
 }
 
diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go
index ce0af936140..893055bccdf 100644
--- a/modules/git/repo_commit_gogit.go
+++ b/modules/git/repo_commit_gogit.go
@@ -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"
 )
 
@@ -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(),
@@ -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
diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go
index d5eb723100a..018e271c394 100644
--- a/modules/git/repo_commit_nogogit.go
+++ b/modules/git/repo_commit_nogogit.go
@@ -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()
 
@@ -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) {
@@ -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
 		}
@@ -131,12 +131,13 @@ 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
 		}
 	}
 
@@ -144,15 +145,15 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
 	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
 }
diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go
index aad725fa9db..8885df4f708 100644
--- a/modules/git/repo_compare.go
+++ b/modules/git/repo_compare.go
@@ -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)
diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go
index 603aabde42a..9bfaa5c02a5 100644
--- a/modules/git/repo_compare_test.go
+++ b/modules/git/repo_compare_test.go
@@ -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"},
 		},
@@ -146,7 +146,7 @@ func TestGetCommitFilesChanged(t *testing.T) {
 			[]string{"file2.txt"},
 		},
 		{
-			EmptyTreeSHA,
+			repo.objectFormat.EmptyTree().String(),
 			"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
 			[]string{"file1.txt", "file2.txt"},
 		},
diff --git a/modules/git/repo_gpg.go b/modules/git/repo_gpg.go
index 4803be58165..e2b45064fd3 100644
--- a/modules/git/repo_gpg.go
+++ b/modules/git/repo_gpg.go
@@ -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
diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go
index 34dd1e01291..6f43734655f 100644
--- a/modules/git/repo_index.go
+++ b/modules/git/repo_index.go
@@ -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
@@ -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])
@@ -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')
 		}
@@ -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
@@ -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
 	}
diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go
index 1d94ad6c00f..b733c119f9e 100644
--- a/modules/git/repo_language_stats_nogogit.go
+++ b/modules/git/repo_language_stats_nogogit.go
@@ -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, ""}
diff --git a/modules/git/repo_object.go b/modules/git/repo_object.go
index 9edc201fead..220fdb38074 100644
--- a/modules/git/repo_object.go
+++ b/modules/git/repo_object.go
@@ -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)
-	if err != nil {
-		return SHA1{}, err
-	}
-	return NewIDFromString(idStr)
+type EmptyReader struct{}
+
+func (EmptyReader) Read(p []byte) (int, error) {
+	return 0, io.EOF
 }
 
-func (repo *Repository) hashObject(reader io.Reader) (string, error) {
-	cmd := NewCommand(repo.Ctx, "hash-object", "-w", "--stdin")
+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 nil, err
+	}
+
+	repo.objectFormat = hash.Type()
+
+	return repo.objectFormat, nil
+}
+
+// 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{
diff --git a/modules/git/repo_ref_gogit.go b/modules/git/repo_ref_gogit.go
index 8a68a4574f1..fc43ce5545d 100644
--- a/modules/git/repo_ref_gogit.go
+++ b/modules/git/repo_ref_gogit.go
@@ -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,
 			}
diff --git a/modules/git/repo_ref_nogogit.go b/modules/git/repo_ref_nogogit.go
index ac53d661b51..c1be60871cf 100644
--- a/modules/git/repo_ref_nogogit.go
+++ b/modules/git/repo_ref_nogogit.go
@@ -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,
 			}
diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go
index ae877f02110..0b08e457cbe 100644
--- a/modules/git/repo_tag.go
+++ b/modules/git/repo_tag.go
@@ -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
 	}
@@ -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
 	}
@@ -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)
 		}
@@ -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)
 	}
@@ -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)
 		}
@@ -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
 	}
diff --git a/modules/git/repo_tag_gogit.go b/modules/git/repo_tag_gogit.go
index 2bc75e7cf9b..c3711ba5a07 100644
--- a/modules/git/repo_tag_gogit.go
+++ b/modules/git/repo_tag_gogit.go
@@ -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()}
@@ -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)
@@ -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
 	}
@@ -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()}
@@ -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,
diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go
index 9080ffcfd78..3cea4894f17 100644
--- a/modules/git/repo_tag_nogogit.go
+++ b/modules/git/repo_tag_nogogit.go
@@ -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"))
@@ -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)
@@ -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
 	}
@@ -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
 	}
diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go
index 4d94efd1acf..c7699f4a7d2 100644
--- a/modules/git/repo_tag_test.go
+++ b/modules/git/repo_tag_test.go
@@ -194,6 +194,7 @@ func TestRepository_GetAnnotatedTag(t *testing.T) {
 }
 
 func TestRepository_parseTagRef(t *testing.T) {
+	sha1 := ObjectFormatFromID(Sha1)
 	tests := []struct {
 		name string
 
@@ -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",
@@ -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",
@@ -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",
@@ -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)
diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go
index 63c33379bf5..9ee80351f0f 100644
--- a/modules/git/repo_tree.go
+++ b/modules/git/repo_tree.go
@@ -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
@@ -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()))
 }
diff --git a/modules/git/repo_tree_gogit.go b/modules/git/repo_tree_gogit.go
index a7b1081b15f..415572e65a1 100644
--- a/modules/git/repo_tree_gogit.go
+++ b/modules/git/repo_tree_gogit.go
@@ -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
 	}
@@ -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
@@ -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 {
diff --git a/modules/git/repo_tree_nogogit.go b/modules/git/repo_tree_nogogit.go
index 4fd77df2b82..f502cc140b7 100644
--- a/modules/git/repo_tree_nogogit.go
+++ b/modules/git/repo_tree_nogogit.go
@@ -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()
 
@@ -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
 		}
@@ -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
 		}
@@ -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
@@ -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
 	}
diff --git a/modules/git/sha1.go b/modules/git/sha1.go
deleted file mode 100644
index 8d6403e8657..00000000000
--- a/modules/git/sha1.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2015 The Gogs Authors. All rights reserved.
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package git
-
-import (
-	"encoding/hex"
-	"fmt"
-	"regexp"
-	"strings"
-)
-
-// EmptySHA defines empty git SHA (undefined, non-existent)
-const EmptySHA = "0000000000000000000000000000000000000000"
-
-// EmptyTreeSHA is the SHA of an empty tree, the root of all git repositories
-const EmptyTreeSHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
-
-// SHAFullLength is the full length of a git SHA
-const SHAFullLength = 40
-
-// SHAPattern can be used to determine if a string is an valid sha
-var shaPattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`)
-
-// IsValidSHAPattern will check if the provided string matches the SHA Pattern
-func IsValidSHAPattern(sha string) bool {
-	return shaPattern.MatchString(sha)
-}
-
-type ErrInvalidSHA struct {
-	SHA string
-}
-
-func (err ErrInvalidSHA) Error() string {
-	return fmt.Sprintf("invalid sha: %s", err.SHA)
-}
-
-// MustID always creates a new SHA1 from a [20]byte array with no validation of input.
-func MustID(b []byte) SHA1 {
-	var id SHA1
-	copy(id[:], b)
-	return id
-}
-
-// NewID creates a new SHA1 from a [20]byte array.
-func NewID(b []byte) (SHA1, error) {
-	if len(b) != 20 {
-		return SHA1{}, fmt.Errorf("Length must be 20: %v", b)
-	}
-	return MustID(b), nil
-}
-
-// MustIDFromString always creates a new sha from a ID with no validation of input.
-func MustIDFromString(s string) SHA1 {
-	b, _ := hex.DecodeString(s)
-	return MustID(b)
-}
-
-// NewIDFromString creates a new SHA1 from a ID string of length 40.
-func NewIDFromString(s string) (SHA1, error) {
-	var id SHA1
-	s = strings.TrimSpace(s)
-	if len(s) != SHAFullLength {
-		return id, fmt.Errorf("Length must be 40: %s", s)
-	}
-	b, err := hex.DecodeString(s)
-	if err != nil {
-		return id, err
-	}
-	return NewID(b)
-}
diff --git a/modules/git/sha1_gogit.go b/modules/git/sha1_gogit.go
deleted file mode 100644
index 28f35d17a98..00000000000
--- a/modules/git/sha1_gogit.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2015 The Gogs Authors. All rights reserved.
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build gogit
-
-package git
-
-import (
-	"github.com/go-git/go-git/v5/plumbing"
-)
-
-// SHA1 a git commit name
-type SHA1 = plumbing.Hash
-
-// ComputeBlobHash compute the hash for a given blob content
-func ComputeBlobHash(content []byte) SHA1 {
-	return plumbing.ComputeHash(plumbing.BlobObject, content)
-}
diff --git a/modules/git/sha1_nogogit.go b/modules/git/sha1_nogogit.go
deleted file mode 100644
index d818d86a3a0..00000000000
--- a/modules/git/sha1_nogogit.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2015 The Gogs Authors. All rights reserved.
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-//go:build !gogit
-
-package git
-
-import (
-	"crypto/sha1"
-	"encoding/hex"
-	"hash"
-	"strconv"
-)
-
-// SHA1 a git commit name
-type SHA1 [20]byte
-
-// String returns a string representation of the SHA
-func (s SHA1) String() string {
-	return hex.EncodeToString(s[:])
-}
-
-// IsZero returns whether this SHA1 is all zeroes
-func (s SHA1) IsZero() bool {
-	var empty SHA1
-	return s == empty
-}
-
-// ComputeBlobHash compute the hash for a given blob content
-func ComputeBlobHash(content []byte) SHA1 {
-	return ComputeHash(ObjectBlob, content)
-}
-
-// ComputeHash compute the hash for a given ObjectType and content
-func ComputeHash(t ObjectType, content []byte) SHA1 {
-	h := NewHasher(t, int64(len(content)))
-	_, _ = h.Write(content)
-	return h.Sum()
-}
-
-// Hasher is a struct that will generate a SHA1
-type Hasher struct {
-	hash.Hash
-}
-
-// NewHasher takes an object type and size and creates a hasher to generate a SHA
-func NewHasher(t ObjectType, size int64) Hasher {
-	h := Hasher{sha1.New()}
-	_, _ = h.Write(t.Bytes())
-	_, _ = h.Write([]byte(" "))
-	_, _ = h.Write([]byte(strconv.FormatInt(size, 10)))
-	_, _ = h.Write([]byte{0})
-	return h
-}
-
-// Sum generates a SHA1 for the provided hash
-func (h Hasher) Sum() (sha1 SHA1) {
-	copy(sha1[:], h.Hash.Sum(nil))
-	return sha1
-}
diff --git a/modules/git/sha1_test.go b/modules/git/sha1_test.go
deleted file mode 100644
index db2944fc53d..00000000000
--- a/modules/git/sha1_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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) {
-	assert.True(t, IsValidSHAPattern("fee1"))
-	assert.True(t, IsValidSHAPattern("abc000"))
-	assert.True(t, IsValidSHAPattern("9023902390239023902390239023902390239023"))
-	assert.False(t, IsValidSHAPattern("90239023902390239023902390239023902390239023"))
-	assert.False(t, IsValidSHAPattern("abc"))
-	assert.False(t, IsValidSHAPattern("123g"))
-	assert.False(t, IsValidSHAPattern("some random text"))
-}
diff --git a/modules/git/tag.go b/modules/git/tag.go
index d0ddef64e08..27358d74f8d 100644
--- a/modules/git/tag.go
+++ b/modules/git/tag.go
@@ -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
@@ -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
@@ -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
 				}
diff --git a/modules/git/tag_test.go b/modules/git/tag_test.go
index 2337e69c6af..129c1e3a023 100644
--- a/modules/git/tag_test.go
+++ b/modules/git/tag_test.go
@@ -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:   "",
@@ -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",
@@ -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)
diff --git a/modules/git/tree.go b/modules/git/tree.go
index 856b8cef53c..1da4a9fa5de 100644
--- a/modules/git/tree.go
+++ b/modules/git/tree.go
@@ -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,
diff --git a/modules/git/tree_blob_gogit.go b/modules/git/tree_blob_gogit.go
index f1afc5d0a6f..92c25cb92c1 100644
--- a/modules/git/tree_blob_gogit.go
+++ b/modules/git/tree_blob_gogit.go
@@ -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
 	}
diff --git a/modules/git/tree_entry_gogit.go b/modules/git/tree_entry_gogit.go
index 194dd12f7db..eb9b0126814 100644
--- a/modules/git/tree_entry_gogit.go
+++ b/modules/git/tree_entry_gogit.go
@@ -14,7 +14,7 @@ import (
 
 // TreeEntry the leaf in the git tree
 type TreeEntry struct {
-	ID SHA1
+	ID ObjectID
 
 	gogitTreeEntry *object.TreeEntry
 	ptree          *Tree
@@ -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(),
 	}
diff --git a/modules/git/tree_entry_nogogit.go b/modules/git/tree_entry_nogogit.go
index cda755886a8..89244e27ee8 100644
--- a/modules/git/tree_entry_nogogit.go
+++ b/modules/git/tree_entry_nogogit.go
@@ -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
 
diff --git a/modules/git/tree_gogit.go b/modules/git/tree_gogit.go
index fa601e6533c..421b0ecb0f0 100644
--- a/modules/git/tree_gogit.go
+++ b/modules/git/tree_gogit.go
@@ -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
@@ -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
 	}
@@ -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,
 		}
@@ -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,
diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go
index ef598d7e913..89d3aebbc0b 100644
--- a/modules/git/tree_nogogit.go
+++ b/modules/git/tree_nogogit.go
@@ -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
@@ -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
 			}
@@ -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
 	}
@@ -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
 	}
diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go
index e4686fa01f4..76cd78e11e6 100644
--- a/modules/indexer/code/git.go
+++ b/modules/indexer/code/git.go
@@ -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
 	}
@@ -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
 }
 
@@ -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
 }
diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go
index 827b2a98493..57f0c90fc6c 100644
--- a/modules/repository/commits_test.go
+++ b/modules/repository/commits_test.go
@@ -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,
@@ -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{
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index 4055029d22a..c143431b7c6 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -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
 	}
 
@@ -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
 	}
 
diff --git a/modules/repository/init.go b/modules/repository/init.go
index 6f791f742b1..a9b5aab16aa 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -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)
@@ -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)
diff --git a/modules/repository/push.go b/modules/repository/push.go
index ea03f9e1537..25695336a52 100644
--- a/modules/repository/push.go
+++ b/modules/repository/push.go
@@ -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
diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go
index 0b259703dea..e7e00dae418 100644
--- a/routers/api/v1/repo/notes.go
+++ b/routers/api/v1/repo/notes.go
@@ -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)
@@ -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
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 64c41d3a97e..1767a7fa674 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -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) {
diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go
index 32f5c85319d..eb82c505440 100644
--- a/routers/api/v1/utils/git.go
+++ b/routers/api/v1/utils/git.go
@@ -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
 	}
diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go
index d5a46e4e7fe..1b274ae154d 100644
--- a/routers/private/hook_post_receive.go
+++ b/routers/private/hook_post_receive.go
@@ -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)
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index 4399e498518..88118097101 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -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),
@@ -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),
@@ -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)
diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go
index 8604789529a..6725205cc6d 100644
--- a/routers/private/hook_verification.go
+++ b/routers/private/hook_verification.go
@@ -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
@@ -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{
@@ -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
 				}
diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go
index cd0c05ff50a..7263ebc4235 100644
--- a/routers/private/hook_verification_test.go
+++ b/routers/private/hook_verification_test.go
@@ -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 {
diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index 52d350ff665..db9be51257e 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -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
 	}
@@ -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
 			}
diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go
index a0bc1dadade..b6de5bf800e 100644
--- a/routers/web/repo/branch.go
+++ b/routers/web/repo/branch.go
@@ -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,
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 3587d287fc5..bd393e7fb7f 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -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()
 	}
 
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index b69af3c61cc..5b1638d3f5f 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -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 {
diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go
index 6ff385f9890..dd47bd79d94 100644
--- a/routers/web/repo/githttp.go
+++ b/routers/web/repo/githttp.go
@@ -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
 		}
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 482fbaac2bc..7a2976f8dca 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -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() {
@@ -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)
diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go
index d478acdde0d..230f1a2a608 100644
--- a/routers/web/repo/setting/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -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)
diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go
index 5100bf782f0..8c232a4cb8d 100644
--- a/routers/web/repo/setting/webhook.go
+++ b/routers/web/repo/setting/webhook.go
@@ -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",
diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go
index 08a7dde67c8..c1fc1eda92a 100644
--- a/services/actions/commit_status.go
+++ b/services/actions/commit_status.go
@@ -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"
@@ -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,
diff --git a/services/agit/agit.go b/services/agit/agit.go
index acfedf09d42..e354b9169a2 100644
--- a/services/agit/agit.go
+++ b/services/agit/agit.go
@@ -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],
@@ -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
diff --git a/services/convert/git_commit_test.go b/services/convert/git_commit_test.go
index 8c4ef88ebe0..d8c1fdfed78 100644
--- a/services/convert/git_commit_test.go
+++ b/services/convert/git_commit_test.go
@@ -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",
@@ -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)
 }
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index 5df7ec8fd60..f6ef97dfdc3 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -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"
@@ -53,6 +54,7 @@ type CreateRepoForm struct {
 	TrustModel      string
 
 	ForkSingleBranch string
+	ObjectFormat     git.ObjectFormat
 }
 
 // Validate validates the fields
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 8bf6cba844d..05d4a0555fc 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -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
@@ -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") {
@@ -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") {
diff --git a/services/migrations/common.go b/services/migrations/common.go
index 4f9837472d9..278c156b036 100644
--- a/services/migrations/common.go
+++ b/services/migrations/common.go
@@ -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
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index 6ad0a2326bd..23d855d615d 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -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
 			}
diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go
index 7c291eabafa..b6c9b814772 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -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))
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index be426d43128..b3ecf2fc54e 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -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)
diff --git a/services/packages/cargo/index.go b/services/packages/cargo/index.go
index 8164ffb01cb..48bd0a4d80a 100644
--- a/services/packages/cargo/index.go
+++ b/services/packages/cargo/index.go
@@ -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 {
diff --git a/services/pull/check.go b/services/pull/check.go
index b51b58f480f..ebe4c6d61b0 100644
--- a/services/pull/check.go
+++ b/services/pull/check.go
@@ -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)
diff --git a/services/pull/merge.go b/services/pull/merge.go
index 33c7455c087..63f0268bebe 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -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")
 		}
 
diff --git a/services/pull/patch.go b/services/pull/patch.go
index 688cbcc027d..1dbbec373d1 100644
--- a/services/pull/patch.go
+++ b/services/pull/patch.go
@@ -129,6 +129,9 @@ func (e *errMergeConflict) Error() string {
 
 func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, gitRepo *git.Repository) error {
 	log.Trace("Attempt to merge:\n%v", file)
+
+	objectFormat, _ := gitRepo.GetObjectFormat()
+
 	switch {
 	case file.stage1 != nil && (file.stage2 == nil || file.stage3 == nil):
 		// 1. Deleted in one or both:
@@ -145,7 +148,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
 		// 2. Added in ours but not in theirs or identical in both
 		//
 		// Not a genuine conflict just add to the index
-		if err := gitRepo.AddObjectToIndex(file.stage2.mode, git.MustIDFromString(file.stage2.sha), file.stage2.path); err != nil {
+		if err := gitRepo.AddObjectToIndex(file.stage2.mode, objectFormat.MustIDFromString(file.stage2.sha), file.stage2.path); err != nil {
 			return err
 		}
 		return nil
@@ -158,7 +161,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
 		// 4. Added in theirs but not ours:
 		//
 		// Not a genuine conflict just add to the index
-		return gitRepo.AddObjectToIndex(file.stage3.mode, git.MustIDFromString(file.stage3.sha), file.stage3.path)
+		return gitRepo.AddObjectToIndex(file.stage3.mode, objectFormat.MustIDFromString(file.stage3.sha), file.stage3.path)
 	case file.stage1 == nil:
 		// 5. Created by new in both
 		//
@@ -219,7 +222,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g
 			return err
 		}
 		hash = strings.TrimSpace(hash)
-		return gitRepo.AddObjectToIndex(file.stage2.mode, git.MustIDFromString(hash), file.stage2.path)
+		return gitRepo.AddObjectToIndex(file.stage2.mode, objectFormat.MustIDFromString(hash), file.stage2.path)
 	default:
 		if file.stage1 != nil {
 			return &errMergeConflict{file.stage1.path}
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 2f5143903aa..a16d1be1c1c 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -327,7 +327,8 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
 			}
 			if err == nil {
 				for _, pr := range prs {
-					if newCommitID != "" && newCommitID != git.EmptySHA {
+					objectFormat, _ := git.GetObjectFormatOfRepo(ctx, pr.BaseRepo.RepoPath())
+					if newCommitID != "" && newCommitID != objectFormat.Empty().String() {
 						changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID)
 						if err != nil {
 							log.Error("checkIfPRContentChanged: %v", err)
diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go
index db32940e383..fde8673a240 100644
--- a/services/pull/temp_repo.go
+++ b/services/pull/temp_repo.go
@@ -93,8 +93,14 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
 
 	baseRepoPath := pr.BaseRepo.RepoPath()
 	headRepoPath := pr.HeadRepo.RepoPath()
+	objectFormat, err := git.GetObjectFormatOfRepo(ctx, baseRepoPath)
+	if err != nil {
+		log.Error("Unable to fetch ObjectFormat of repository %s: %v", baseRepoPath, err)
+		cancel()
+		return nil, nil, err
+	}
 
-	if err := git.InitRepository(ctx, tmpBasePath, false); err != nil {
+	if err := git.InitRepository(ctx, tmpBasePath, false, objectFormat); err != nil {
 		log.Error("Unable to init tmpBasePath for %-v: %v", pr, err)
 		cancel()
 		return nil, nil, err
@@ -172,7 +178,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
 	var headBranch string
 	if pr.Flow == issues_model.PullRequestFlowGithub {
 		headBranch = git.BranchPrefix + pr.HeadBranch
-	} else if len(pr.HeadCommitID) == git.SHAFullLength { // for not created pull request
+	} else if len(pr.HeadCommitID) == objectFormat.FullLength() { // for not created pull request
 		headBranch = pr.HeadCommitID
 	} else {
 		headBranch = pr.GetGitRefName()
diff --git a/services/release/release.go b/services/release/release.go
index 3ba2a3f6117..4cd520e82f9 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -86,16 +86,17 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel
 			created = true
 			rel.LowerTagName = strings.ToLower(rel.TagName)
 
+			objectFormat, _ := gitRepo.GetObjectFormat()
 			commits := repository.NewPushCommits()
 			commits.HeadCommit = repository.CommitToPushCommit(commit)
-			commits.CompareURL = rel.Repo.ComposeCompareURL(git.EmptySHA, commit.ID.String())
+			commits.CompareURL = rel.Repo.ComposeCompareURL(objectFormat.Empty().String(), commit.ID.String())
 
 			refFullName := git.RefNameFromTag(rel.TagName)
 			notify_service.PushCommits(
 				ctx, rel.Publisher, rel.Repo,
 				&repository.PushUpdateOptions{
 					RefFullName: refFullName,
-					OldCommitID: git.EmptySHA,
+					OldCommitID: objectFormat.Empty().String(),
 					NewCommitID: commit.ID.String(),
 				}, commits)
 			notify_service.CreateRef(ctx, rel.Publisher, rel.Repo, refFullName, commit.ID.String())
@@ -325,12 +326,16 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re
 		}
 
 		refName := git.RefNameFromTag(rel.TagName)
+		objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath())
+		if err != nil {
+			return err
+		}
 		notify_service.PushCommits(
 			ctx, doer, repo,
 			&repository.PushUpdateOptions{
 				RefFullName: refName,
 				OldCommitID: rel.Sha1,
-				NewCommitID: git.EmptySHA,
+				NewCommitID: objectFormat.Empty().String(),
 			}, repository.NewPushCommits())
 		notify_service.DeleteRef(ctx, doer, repo, refName)
 
diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go
index 9f1ea48dca4..f700e3af5de 100644
--- a/services/repository/archiver/archiver.go
+++ b/services/repository/archiver/archiver.go
@@ -9,7 +9,6 @@ import (
 	"fmt"
 	"io"
 	"os"
-	"regexp"
 	"strings"
 	"time"
 
@@ -36,10 +35,6 @@ type ArchiveRequest struct {
 	CommitID string
 }
 
-// SHA1 hashes will only go up to 40 characters, but SHA256 hashes will go all
-// the way to 64.
-var shaRegex = regexp.MustCompile(`^[0-9a-f]{4,64}$`)
-
 // ErrUnknownArchiveFormat request archive format is not supported
 type ErrUnknownArchiveFormat struct {
 	RequestFormat string
@@ -96,30 +91,13 @@ func NewRequest(repoID int64, repo *git.Repository, uri string) (*ArchiveRequest
 
 	r.refName = strings.TrimSuffix(uri, ext)
 
-	var err error
 	// Get corresponding commit.
-	if repo.IsBranchExist(r.refName) {
-		r.CommitID, err = repo.GetBranchCommitID(r.refName)
-		if err != nil {
-			return nil, err
-		}
-	} else if repo.IsTagExist(r.refName) {
-		r.CommitID, err = repo.GetTagCommitID(r.refName)
-		if err != nil {
-			return nil, err
-		}
-	} else if shaRegex.MatchString(r.refName) {
-		if repo.IsCommitExist(r.refName) {
-			r.CommitID = r.refName
-		} else {
-			return nil, git.ErrNotExist{
-				ID: r.refName,
-			}
-		}
-	} else {
+	commitID, err := repo.ConvertToGitID(r.refName)
+	if err != nil {
 		return nil, RepoRefNotFoundError{RefName: r.refName}
 	}
 
+	r.CommitID = commitID.String()
 	return r, nil
 }
 
diff --git a/services/repository/branch.go b/services/repository/branch.go
index b3dbb252ca3..b797917757e 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -377,6 +377,11 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
 		return fmt.Errorf("GetBranch: %vc", err)
 	}
 
+	objectFormat, err := gitRepo.GetObjectFormat()
+	if err != nil {
+		return err
+	}
+
 	if rawBranch.IsDeleted {
 		return nil
 	}
@@ -403,7 +408,7 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
 		&repo_module.PushUpdateOptions{
 			RefFullName:  git.RefNameFromBranch(branchName),
 			OldCommitID:  commit.ID.String(),
-			NewCommitID:  git.EmptySHA,
+			NewCommitID:  objectFormat.Empty().String(),
 			PusherID:     doer.ID,
 			PusherName:   doer.Name,
 			RepoUserName: repo.OwnerName,
diff --git a/services/repository/check.go b/services/repository/check.go
index 2f26d030c33..23c4f79bf20 100644
--- a/services/repository/check.go
+++ b/services/repository/check.go
@@ -192,7 +192,7 @@ func ReinitMissingRepositories(ctx context.Context) error {
 		default:
 		}
 		log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
-		if err := git.InitRepository(ctx, repo.RepoPath(), true); err != nil {
+		if err := git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormat); err != nil {
 			log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err)
 			if err2 := system_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil {
 				log.Error("CreateRepositoryNotice: %v", err2)
diff --git a/services/repository/create.go b/services/repository/create.go
index b6b6454c441..a41904eb7cd 100644
--- a/services/repository/create.go
+++ b/services/repository/create.go
@@ -43,6 +43,7 @@ type CreateRepoOptions struct {
 	Status         repo_model.RepositoryStatus
 	TrustModel     repo_model.TrustModelType
 	MirrorInterval string
+	ObjectFormat   git.ObjectFormat
 }
 
 func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
@@ -134,7 +135,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
 
 // InitRepository initializes README and .gitignore if needed.
 func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
-	if err = repo_module.CheckInitRepository(ctx, repo.OwnerName, repo.Name); err != nil {
+	if err = repo_module.CheckInitRepository(ctx, repo.OwnerName, repo.Name, opts.ObjectFormat); err != nil {
 		return err
 	}
 
@@ -209,6 +210,10 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
 		opts.DefaultBranch = setting.Repository.DefaultBranch
 	}
 
+	if opts.ObjectFormat == nil {
+		opts.ObjectFormat = git.ObjectFormatFromID(git.Sha1)
+	}
+
 	// Check if label template exist
 	if len(opts.IssueLabels) > 0 {
 		if _, err := repo_module.LoadTemplateLabelsByDisplayName(opts.IssueLabels); err != nil {
@@ -234,6 +239,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
 		TrustModel:                      opts.TrustModel,
 		IsMirror:                        opts.IsMirror,
 		DefaultBranch:                   opts.DefaultBranch,
+		ObjectFormat:                    opts.ObjectFormat,
 	}
 
 	var rollbackRepo *repo_model.Repository
diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go
index c1c5bfb6176..0085e88d552 100644
--- a/services/repository/files/cherry_pick.go
+++ b/services/repository/files/cherry_pick.go
@@ -11,7 +11,6 @@ import (
 	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/services/pull"
@@ -48,7 +47,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
 	if opts.LastCommitID == "" {
 		opts.LastCommitID = commit.ID.String()
 	} else {
-		lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID)
+		lastCommitID, err := t.gitRepo.ConvertToGitID(opts.LastCommitID)
 		if err != nil {
 			return nil, fmt.Errorf("CherryPick: Invalid last commit ID: %w", err)
 		}
@@ -67,7 +66,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
 	}
 	parent, err := commit.ParentID(0)
 	if err != nil {
-		parent = git.MustIDFromString(git.EmptyTreeSHA)
+		parent = repo.ObjectFormat.EmptyTree()
 	}
 
 	base, right := parent.String(), commit.ID.String()
diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go
index 3e4627487be..048e41e6fdd 100644
--- a/services/repository/files/commit.go
+++ b/services/repository/files/commit.go
@@ -29,10 +29,15 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
 	}
 	defer closer.Close()
 
-	if commit, err := gitRepo.GetCommit(sha); err != nil {
+	objectFormat, err := gitRepo.GetObjectFormat()
+	if err != nil {
+		return fmt.Errorf("GetObjectFormat[%s]: %w", repoPath, err)
+	}
+	commit, err := gitRepo.GetCommit(sha)
+	if err != nil {
 		gitRepo.Close()
 		return fmt.Errorf("GetCommit[%s]: %w", sha, err)
-	} else if len(sha) != git.SHAFullLength {
+	} else if len(sha) != objectFormat.FullLength() {
 		// use complete commit sha
 		sha = commit.ID.String()
 	}
@@ -41,7 +46,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
 	if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
 		Repo:         repo,
 		Creator:      creator,
-		SHA:          sha,
+		SHA:          commit.ID,
 		CommitStatus: status,
 	}); err != nil {
 		return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err)
diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go
index 5ef0619636e..96899e6e0e9 100644
--- a/services/repository/files/patch.go
+++ b/services/repository/files/patch.go
@@ -130,7 +130,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
 	if opts.LastCommitID == "" {
 		opts.LastCommitID = commit.ID.String()
 	} else {
-		lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID)
+		lastCommitID, err := t.gitRepo.ConvertToGitID(opts.LastCommitID)
 		if err != nil {
 			return nil, fmt.Errorf("ApplyPatch: Invalid last commit ID: %w", err)
 		}
diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index be13b2016a1..0b5aaba1544 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -77,8 +77,8 @@ func (t *TemporaryUploadRepository) Clone(branch string) error {
 }
 
 // Init the repository
-func (t *TemporaryUploadRepository) Init() error {
-	if err := git.InitRepository(t.ctx, t.basePath, false); err != nil {
+func (t *TemporaryUploadRepository) Init(objectFormat git.ObjectFormat) error {
+	if err := git.InitRepository(t.ctx, t.basePath, false, objectFormat); err != nil {
 		return err
 	}
 	gitRepo, err := git.OpenRepository(t.ctx, t.basePath)
diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go
index 0b1d304845e..9d3185c3fc4 100644
--- a/services/repository/files/tree.go
+++ b/services/repository/files/tree.go
@@ -37,19 +37,21 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
 	}
 	apiURL := repo.APIURL()
 	apiURLLen := len(apiURL)
+	objectFormat, _ := gitRepo.GetObjectFormat()
+	hashLen := objectFormat.FullLength()
 
-	// 51 is len(sha1) + len("/git/blobs/"). 40 + 11.
-	blobURL := make([]byte, apiURLLen+51)
+	const gitBlobsPath = "/git/blobs/"
+	blobURL := make([]byte, apiURLLen+hashLen+len(gitBlobsPath))
 	copy(blobURL, apiURL)
-	copy(blobURL[apiURLLen:], "/git/blobs/")
+	copy(blobURL[apiURLLen:], []byte(gitBlobsPath))
 
-	// 51 is len(sha1) + len("/git/trees/"). 40 + 11.
-	treeURL := make([]byte, apiURLLen+51)
+	const gitTreePath = "/git/trees/"
+	treeURL := make([]byte, apiURLLen+hashLen+len(gitTreePath))
 	copy(treeURL, apiURL)
-	copy(treeURL[apiURLLen:], "/git/trees/")
+	copy(treeURL[apiURLLen:], []byte(gitTreePath))
 
-	// 40 is the size of the sha1 hash in hexadecimal format.
-	copyPos := len(treeURL) - git.SHAFullLength
+	// copyPos is at the start of the hash
+	copyPos := len(treeURL) - hashLen
 
 	if perPage <= 0 || perPage > setting.API.DefaultGitTreesPerPage {
 		perPage = setting.API.DefaultGitTreesPerPage
diff --git a/services/repository/files/update.go b/services/repository/files/update.go
index 42b98a27394..d202717ef57 100644
--- a/services/repository/files/update.go
+++ b/services/repository/files/update.go
@@ -155,7 +155,8 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 		if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
 			return nil, err
 		}
-		if err := t.Init(); err != nil {
+		objectFormat, _ := gitRepo.GetObjectFormat()
+		if err := t.Init(objectFormat); err != nil {
 			return nil, err
 		}
 		hasOldBranch = false
@@ -202,7 +203,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 		if opts.LastCommitID == "" {
 			opts.LastCommitID = commit.ID.String()
 		} else {
-			lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID)
+			lastCommitID, err := t.gitRepo.ConvertToGitID(opts.LastCommitID)
 			if err != nil {
 				return nil, fmt.Errorf("ConvertToSHA1: Invalid last commit ID: %w", err)
 			}
diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go
index 6a1f2ccd16c..8be87735447 100644
--- a/services/repository/files/upload.go
+++ b/services/repository/files/upload.go
@@ -91,7 +91,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 		if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
 			return err
 		}
-		if err = t.Init(); err != nil {
+		if err = t.Init(repo.ObjectFormat); err != nil {
 			return err
 		}
 		hasOldBranch = false
diff --git a/services/repository/lfs.go b/services/repository/lfs.go
index 8e654b6f13b..b437fda15d5 100644
--- a/services/repository/lfs.go
+++ b/services/repository/lfs.go
@@ -78,13 +78,14 @@ func GarbageCollectLFSMetaObjectsForRepo(ctx context.Context, repo *repo_model.R
 
 	store := lfs.NewContentStore()
 	errStop := errors.New("STOPERR")
+	objectFormat, _ := gitRepo.GetObjectFormat()
 
 	err = git_model.IterateLFSMetaObjectsForRepo(ctx, repo.ID, func(ctx context.Context, metaObject *git_model.LFSMetaObject, count int64) error {
 		if opts.NumberToCheckPerRepo > 0 && total > opts.NumberToCheckPerRepo {
 			return errStop
 		}
 		total++
-		pointerSha := git.ComputeBlobHash([]byte(metaObject.Pointer.StringContent()))
+		pointerSha := git.ComputeBlobHash(objectFormat, []byte(metaObject.Pointer.StringContent()))
 
 		if gitRepo.IsObjectExist(pointerSha.String()) {
 			return git_model.MarkLFSMetaObject(ctx, metaObject.ID)
diff --git a/services/repository/push.go b/services/repository/push.go
index b5388834c0b..3003933c34b 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -65,7 +65,7 @@ func PushUpdates(opts []*repo_module.PushUpdateOptions) error {
 
 	for _, opt := range opts {
 		if opt.IsNewRef() && opt.IsDelRef() {
-			return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
+			return fmt.Errorf("Old and new revisions are both NULL")
 		}
 	}
 
@@ -94,6 +94,11 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 	}
 	defer gitRepo.Close()
 
+	objectFormat, err := gitRepo.GetObjectFormat()
+	if err != nil {
+		return fmt.Errorf("unknown repository ObjectFormat [%s]: %w", repoPath, err)
+	}
+
 	if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
 		return fmt.Errorf("Failed to update size for repository: %v", err)
 	}
@@ -106,7 +111,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 		log.Trace("pushUpdates: %-v %s %s %s", repo, opts.OldCommitID, opts.NewCommitID, opts.RefFullName)
 
 		if opts.IsNewRef() && opts.IsDelRef() {
-			return fmt.Errorf("old and new revisions are both %s", git.EmptySHA)
+			return fmt.Errorf("old and new revisions are both %s", objectFormat.Empty())
 		}
 		if opts.RefFullName.IsTag() {
 			if pusher == nil || pusher.ID != opts.PusherID {
@@ -126,7 +131,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 					&repo_module.PushUpdateOptions{
 						RefFullName: git.RefNameFromTag(tagName),
 						OldCommitID: opts.OldCommitID,
-						NewCommitID: git.EmptySHA,
+						NewCommitID: objectFormat.Empty().String(),
 					}, repo_module.NewPushCommits())
 
 				delTags = append(delTags, tagName)
@@ -139,13 +144,13 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 
 				commits := repo_module.NewPushCommits()
 				commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
-				commits.CompareURL = repo.ComposeCompareURL(git.EmptySHA, opts.NewCommitID)
+				commits.CompareURL = repo.ComposeCompareURL(objectFormat.Empty().String(), opts.NewCommitID)
 
 				notify_service.PushCommits(
 					ctx, pusher, repo,
 					&repo_module.PushUpdateOptions{
 						RefFullName: opts.RefFullName,
-						OldCommitID: git.EmptySHA,
+						OldCommitID: objectFormat.Empty().String(),
 						NewCommitID: opts.NewCommitID,
 					}, commits)
 
@@ -229,7 +234,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 				}
 
 				oldCommitID := opts.OldCommitID
-				if oldCommitID == git.EmptySHA && len(commits.Commits) > 0 {
+				if oldCommitID == objectFormat.Empty().String() && len(commits.Commits) > 0 {
 					oldCommit, err := gitRepo.GetCommit(commits.Commits[len(commits.Commits)-1].Sha1)
 					if err != nil && !git.IsErrNotExist(err) {
 						log.Error("unable to GetCommit %s from %-v: %v", oldCommitID, repo, err)
@@ -245,11 +250,11 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 					}
 				}
 
-				if oldCommitID == git.EmptySHA && repo.DefaultBranch != branch {
+				if oldCommitID == objectFormat.Empty().String() && repo.DefaultBranch != branch {
 					oldCommitID = repo.DefaultBranch
 				}
 
-				if oldCommitID != git.EmptySHA {
+				if oldCommitID != objectFormat.Empty().String() {
 					commits.CompareURL = repo.ComposeCompareURL(oldCommitID, opts.NewCommitID)
 				} else {
 					commits.CompareURL = ""
diff --git a/services/webhook/slack.go b/services/webhook/slack.go
index ac27b5bc71b..945b0662d84 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -92,6 +92,7 @@ func SlackLinkFormatter(url, text string) string {
 
 // SlackLinkToRef slack-formatter link to a repo ref
 func SlackLinkToRef(repoURL, ref string) string {
+	// FIXME: SHA1 hardcoded here
 	url := git.RefURL(repoURL, ref)
 	refName := git.RefName(ref).ShortName()
 	return SlackLinkFormatter(url, refName)
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 18371efd098..ecda926ec1e 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -36,7 +36,7 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error {
 		return nil
 	}
 
-	if err := git.InitRepository(ctx, repo.WikiPath(), true); err != nil {
+	if err := git.InitRepository(ctx, repo.WikiPath(), true, git.ObjectFormatFromID(git.Sha1)); err != nil {
 		return fmt.Errorf("InitRepository: %w", err)
 	} else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
 		return fmt.Errorf("createDelegateHooks: %w", err)
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index e8da176a08d..9981fb42583 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -302,7 +302,7 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) {
 	// Now create a temporaryDirectory
 	tmpDir := t.TempDir()
 
-	err := git.InitRepository(git.DefaultContext, tmpDir, true)
+	err := git.InitRepository(git.DefaultContext, tmpDir, true, git.ObjectFormatFromID(git.Sha1))
 	assert.NoError(t, err)
 
 	gitRepo, err := git.OpenRepository(git.DefaultContext, tmpDir)
diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go
index 10cf79b9fd8..de671dec19e 100644
--- a/tests/integration/git_helper_for_declarative_test.go
+++ b/tests/integration/git_helper_for_declarative_test.go
@@ -120,7 +120,7 @@ func doGitCloneFail(u *url.URL) func(*testing.T) {
 func doGitInitTestRepository(dstPath string) func(*testing.T) {
 	return func(t *testing.T) {
 		// Init repository in dstPath
-		assert.NoError(t, git.InitRepository(git.DefaultContext, dstPath, false))
+		assert.NoError(t, git.InitRepository(git.DefaultContext, dstPath, false, git.ObjectFormatFromID(git.Sha1)))
 		// forcibly set default branch to master
 		_, _, err := git.NewCommand(git.DefaultContext, "symbolic-ref", "HEAD", git.BranchPrefix+"master").RunStdString(&git.RunOpts{Dir: dstPath})
 		assert.NoError(t, err)