mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Abstract hash function usage (#28138)
Refactor Hash interfaces and centralize hash function. This will allow easier introduction of different hash function later on. This forms the "no-op" part of the SHA256 enablement patch.
This commit is contained in:
		@@ -376,7 +376,9 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
		oldCommitIDs[count] = string(fields[0])
 | 
							oldCommitIDs[count] = string(fields[0])
 | 
				
			||||||
		newCommitIDs[count] = string(fields[1])
 | 
							newCommitIDs[count] = string(fields[1])
 | 
				
			||||||
		refFullNames[count] = git.RefName(fields[2])
 | 
							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
 | 
								masterPushed = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		count++
 | 
							count++
 | 
				
			||||||
@@ -669,7 +671,8 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								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))
 | 
								err = writeDataPktLine(ctx, os.Stdout, []byte("option old-oid "+rs.OldOID))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,7 @@ func TestAddDeletedBranch(t *testing.T) {
 | 
				
			|||||||
	assert.True(t, secondBranch.IsDeleted)
 | 
						assert.True(t, secondBranch.IsDeleted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	commit := &git.Commit{
 | 
						commit := &git.Commit{
 | 
				
			||||||
		ID:            git.MustIDFromString(secondBranch.CommitID),
 | 
							ID:            repo.ObjectFormat.MustIDFromString(secondBranch.CommitID),
 | 
				
			||||||
		CommitMessage: secondBranch.CommitMessage,
 | 
							CommitMessage: secondBranch.CommitMessage,
 | 
				
			||||||
		Committer: &git.Signature{
 | 
							Committer: &git.Signature{
 | 
				
			||||||
			When: secondBranch.CommitTime.AsLocalTime(),
 | 
								When: secondBranch.CommitTime.AsLocalTime(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,7 +114,8 @@ WHEN NOT MATCHED
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetNextCommitStatusIndex retried 3 times to generate a resource index
 | 
					// GetNextCommitStatusIndex retried 3 times to generate a resource index
 | 
				
			||||||
func GetNextCommitStatusIndex(ctx context.Context, repoID int64, sha string) (int64, error) {
 | 
					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}
 | 
							return 0, git.ErrInvalidSHA{SHA: sha}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -425,7 +426,7 @@ func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, befor
 | 
				
			|||||||
type NewCommitStatusOptions struct {
 | 
					type NewCommitStatusOptions struct {
 | 
				
			||||||
	Repo         *repo_model.Repository
 | 
						Repo         *repo_model.Repository
 | 
				
			||||||
	Creator      *user_model.User
 | 
						Creator      *user_model.User
 | 
				
			||||||
	SHA          string
 | 
						SHA          git.ObjectID
 | 
				
			||||||
	CommitStatus *CommitStatus
 | 
						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)
 | 
							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)
 | 
						ctx, committer, err := db.TxContext(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						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)
 | 
							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()
 | 
						defer committer.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get the next Status Index
 | 
						// 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 {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("generate commit status index failed: %w", err)
 | 
							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.Description = strings.TrimSpace(opts.CommitStatus.Description)
 | 
				
			||||||
	opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
 | 
						opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
 | 
				
			||||||
	opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
 | 
						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.CreatorID = opts.Creator.ID
 | 
				
			||||||
	opts.CommitStatus.RepoID = opts.Repo.ID
 | 
						opts.CommitStatus.RepoID = opts.Repo.ID
 | 
				
			||||||
	opts.CommitStatus.Index = idx
 | 
						opts.CommitStatus.Index = idx
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models/unit"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -179,6 +180,7 @@ type Repository struct {
 | 
				
			|||||||
	IsFsckEnabled                   bool               `xorm:"NOT NULL DEFAULT true"`
 | 
						IsFsckEnabled                   bool               `xorm:"NOT NULL DEFAULT true"`
 | 
				
			||||||
	CloseIssuesViaCommitInAnyBranch bool               `xorm:"NOT NULL DEFAULT false"`
 | 
						CloseIssuesViaCommitInAnyBranch bool               `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
	Topics                          []string           `xorm:"TEXT JSON"`
 | 
						Topics                          []string           `xorm:"TEXT JSON"`
 | 
				
			||||||
 | 
						ObjectFormat                    git.ObjectFormat   `xorm:"-"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TrustModel TrustModelType
 | 
						TrustModel TrustModelType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -274,6 +276,8 @@ func (repo *Repository) AfterLoad() {
 | 
				
			|||||||
	repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
 | 
						repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
 | 
				
			||||||
	repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects
 | 
						repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects
 | 
				
			||||||
	repo.NumOpenActionRuns = repo.NumActionRuns - repo.NumClosedActionRuns
 | 
						repo.NumOpenActionRuns = repo.NumActionRuns - repo.NumClosedActionRuns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo.ObjectFormat = git.ObjectFormatFromID(git.Sha1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadAttributes loads attributes of the repository.
 | 
					// LoadAttributes loads attributes of the repository.
 | 
				
			||||||
@@ -313,7 +317,7 @@ func (repo *Repository) HTMLURL() string {
 | 
				
			|||||||
// CommitLink make link to by commit full ID
 | 
					// CommitLink make link to by commit full ID
 | 
				
			||||||
// note: won't check whether it's an right id
 | 
					// note: won't check whether it's an right id
 | 
				
			||||||
func (repo *Repository) CommitLink(commitID string) (result string) {
 | 
					func (repo *Repository) CommitLink(commitID string) (result string) {
 | 
				
			||||||
	if commitID == "" || commitID == "0000000000000000000000000000000000000000" {
 | 
						if git.IsEmptyCommitID(commitID) {
 | 
				
			||||||
		result = ""
 | 
							result = ""
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		result = repo.Link() + "/commit/" + url.PathEscape(commitID)
 | 
							result = repo.Link() + "/commit/" + url.PathEscape(commitID)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -308,6 +308,12 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 | 
				
			|||||||
			return
 | 
								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 {
 | 
							if ref := ctx.FormTrim("ref"); len(ref) > 0 {
 | 
				
			||||||
			commit, err := ctx.Repo.GitRepo.GetCommit(ref)
 | 
								commit, err := ctx.Repo.GitRepo.GetCommit(ref)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
@@ -325,7 +331,6 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var err error
 | 
					 | 
				
			||||||
		refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
 | 
							refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
							if ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
				
			||||||
@@ -342,7 +347,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 | 
				
			|||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
								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.CommitID = refName
 | 
				
			||||||
			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
 | 
								ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -825,7 +825,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		// For legacy and API support only full commit sha
 | 
							// For legacy and API support only full commit sha
 | 
				
			||||||
		parts := strings.Split(path, "/")
 | 
							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:], "/")
 | 
								repo.TreePath = strings.Join(parts[1:], "/")
 | 
				
			||||||
			return parts[0]
 | 
								return parts[0]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -869,7 +871,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 | 
				
			|||||||
		return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
 | 
							return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
 | 
				
			||||||
	case RepoRefCommit:
 | 
						case RepoRefCommit:
 | 
				
			||||||
		parts := strings.Split(path, "/")
 | 
							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:], "/")
 | 
								repo.TreePath = strings.Join(parts[1:], "/")
 | 
				
			||||||
			return parts[0]
 | 
								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.
 | 
							// Get default branch.
 | 
				
			||||||
		if len(ctx.Params("*")) == 0 {
 | 
							if len(ctx.Params("*")) == 0 {
 | 
				
			||||||
			refName = ctx.Repo.Repository.DefaultBranch
 | 
								refName = ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
@@ -995,7 +1005,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 | 
				
			|||||||
					return cancel
 | 
										return cancel
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
									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.IsViewCommit = true
 | 
				
			||||||
				ctx.Repo.CommitID = refName
 | 
									ctx.Repo.CommitID = refName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1005,7 +1015,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 | 
				
			|||||||
					return cancel
 | 
										return cancel
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				// If short commit ID add canonical link header
 | 
									// 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\"",
 | 
										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))))
 | 
											util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -148,7 +148,7 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
 | 
				
			|||||||
// ReadBatchLine reads the header line from cat-file --batch
 | 
					// ReadBatchLine reads the header line from cat-file --batch
 | 
				
			||||||
// We expect:
 | 
					// We expect:
 | 
				
			||||||
// <sha> SP <type> SP <size> LF
 | 
					// <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) {
 | 
					func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) {
 | 
				
			||||||
	typ, err = rd.ReadString('\n')
 | 
						typ, err = rd.ReadString('\n')
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -251,20 +251,19 @@ headerLoop:
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// git tree files are a list:
 | 
					// 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
 | 
					// 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"
 | 
					const hextable = "0123456789abcdef"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// To40ByteSHA converts a 20-byte SHA into a 40-byte sha. Input and output can be the
 | 
					// BinToHexHeash converts a binary Hash into a hex encoded one. Input and output can be the
 | 
				
			||||||
// same 40 byte slice to support in place conversion without allocations.
 | 
					// same byte slice to support in place conversion without allocations.
 | 
				
			||||||
// This is at least 100x quicker that hex.EncodeToString
 | 
					// This is at least 100x quicker that hex.EncodeToString
 | 
				
			||||||
// NB This requires that out is a 40-byte slice
 | 
					func BinToHex(objectFormat ObjectFormat, sha, out []byte) []byte {
 | 
				
			||||||
func To40ByteSHA(sha, out []byte) []byte {
 | 
						for i := objectFormat.FullLength()/2 - 1; i >= 0; i-- {
 | 
				
			||||||
	for i := 19; i >= 0; i-- {
 | 
					 | 
				
			||||||
		v := sha[i]
 | 
							v := sha[i]
 | 
				
			||||||
		vhi, vlo := v>>4, v&0x0f
 | 
							vhi, vlo := v>>4, v&0x0f
 | 
				
			||||||
		shi, slo := hextable[vhi], hextable[vlo]
 | 
							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
 | 
					// It is recommended therefore to pass in an fnameBuf large enough to avoid almost all allocations
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Each line is composed of:
 | 
					// 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
 | 
					// We don't attempt to convert the raw HASH to save a lot of time
 | 
				
			||||||
func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fname, sha []byte, n int, err error) {
 | 
					func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fname, sha []byte, n int, err error) {
 | 
				
			||||||
	var readBytes []byte
 | 
						var readBytes []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Read the Mode & fname
 | 
						// Read the Mode & fname
 | 
				
			||||||
@@ -324,11 +323,12 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
 | 
				
			|||||||
	fnameBuf = fnameBuf[:len(fnameBuf)-1]
 | 
						fnameBuf = fnameBuf[:len(fnameBuf)-1]
 | 
				
			||||||
	fname = fnameBuf
 | 
						fname = fnameBuf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Deal with the 20-byte SHA
 | 
						// Deal with the binary hash
 | 
				
			||||||
	idx = 0
 | 
						idx = 0
 | 
				
			||||||
	for idx < 20 {
 | 
						len := objectFormat.FullLength() / 2
 | 
				
			||||||
 | 
						for idx < len {
 | 
				
			||||||
		var read int
 | 
							var read int
 | 
				
			||||||
		read, err = rd.Read(shaBuf[idx:20])
 | 
							read, err = rd.Read(shaBuf[idx:len])
 | 
				
			||||||
		n += read
 | 
							n += read
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return mode, fname, sha, n, err
 | 
								return mode, fname, sha, n, err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,6 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"regexp"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -33,14 +31,13 @@ type BlameReader struct {
 | 
				
			|||||||
	done           chan error
 | 
						done           chan error
 | 
				
			||||||
	lastSha        *string
 | 
						lastSha        *string
 | 
				
			||||||
	ignoreRevsFile *string
 | 
						ignoreRevsFile *string
 | 
				
			||||||
 | 
						objectFormat   ObjectFormat
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *BlameReader) UsesIgnoreRevs() bool {
 | 
					func (r *BlameReader) UsesIgnoreRevs() bool {
 | 
				
			||||||
	return r.ignoreRevsFile != nil
 | 
						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)
 | 
					// NextPart returns next part of blame (sequential code lines with the same commit)
 | 
				
			||||||
func (r *BlameReader) NextPart() (*BlamePart, error) {
 | 
					func (r *BlameReader) NextPart() (*BlamePart, error) {
 | 
				
			||||||
	var blamePart *BlamePart
 | 
						var blamePart *BlamePart
 | 
				
			||||||
@@ -52,6 +49,7 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const previousHeader = "previous "
 | 
				
			||||||
	var lineBytes []byte
 | 
						var lineBytes []byte
 | 
				
			||||||
	var isPrefix bool
 | 
						var isPrefix bool
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
@@ -67,21 +65,22 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		line := string(lineBytes)
 | 
							var objectID string
 | 
				
			||||||
 | 
							objectFormatLength := r.objectFormat.FullLength()
 | 
				
			||||||
		lines := shaLineRegex.FindStringSubmatch(line)
 | 
					 | 
				
			||||||
		if lines != nil {
 | 
					 | 
				
			||||||
			sha1 := lines[1]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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 {
 | 
								if blamePart == nil {
 | 
				
			||||||
				blamePart = &BlamePart{
 | 
									blamePart = &BlamePart{
 | 
				
			||||||
					Sha:   sha1,
 | 
										Sha:   objectID,
 | 
				
			||||||
					Lines: make([]string, 0),
 | 
										Lines: make([]string, 0),
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if blamePart.Sha != sha1 {
 | 
								if blamePart.Sha != objectID {
 | 
				
			||||||
				r.lastSha = &sha1
 | 
									r.lastSha = &objectID
 | 
				
			||||||
				// need to munch to end of line...
 | 
									// need to munch to end of line...
 | 
				
			||||||
				for isPrefix {
 | 
									for isPrefix {
 | 
				
			||||||
					_, isPrefix, err = r.bufferedReader.ReadLine()
 | 
										_, isPrefix, err = r.bufferedReader.ReadLine()
 | 
				
			||||||
@@ -91,12 +90,13 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				return blamePart, nil
 | 
									return blamePart, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if line[0] == '\t' {
 | 
							} else if lineBytes[0] == '\t' {
 | 
				
			||||||
			blamePart.Lines = append(blamePart.Lines, line[1:])
 | 
								blamePart.Lines = append(blamePart.Lines, string(lineBytes[1:]))
 | 
				
			||||||
		} else if strings.HasPrefix(line, "previous ") {
 | 
							} else if bytes.HasPrefix(lineBytes, []byte(previousHeader)) {
 | 
				
			||||||
			parts := strings.SplitN(line[len("previous "):], " ", 2)
 | 
								offset := len(previousHeader) // already includes a space
 | 
				
			||||||
			blamePart.PreviousSha = parts[0]
 | 
								blamePart.PreviousSha = string(lineBytes[offset : offset+objectFormatLength])
 | 
				
			||||||
			blamePart.PreviousPath = parts[1]
 | 
								offset += objectFormatLength + 1 // +1 for space
 | 
				
			||||||
 | 
								blamePart.PreviousPath = string(lineBytes[offset:])
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// need to munch to end of line...
 | 
							// 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
 | 
					// 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
 | 
						var ignoreRevsFile *string
 | 
				
			||||||
	if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore {
 | 
						if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore {
 | 
				
			||||||
		ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
 | 
							ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
 | 
				
			||||||
@@ -175,6 +175,7 @@ func CreateBlameReader(ctx context.Context, repoPath string, commit *Commit, fil
 | 
				
			|||||||
		bufferedReader: bufferedReader,
 | 
							bufferedReader: bufferedReader,
 | 
				
			||||||
		done:           done,
 | 
							done:           done,
 | 
				
			||||||
		ignoreRevsFile: ignoreRevsFile,
 | 
							ignoreRevsFile: ignoreRevsFile,
 | 
				
			||||||
 | 
							objectFormat:   objectFormat,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ func TestReadingBlameOutput(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, bypass := range []bool{false, true} {
 | 
							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.NoError(t, err)
 | 
				
			||||||
			assert.NotNil(t, blameReader)
 | 
								assert.NotNil(t, blameReader)
 | 
				
			||||||
			defer blameReader.Close()
 | 
								defer blameReader.Close()
 | 
				
			||||||
@@ -122,7 +122,7 @@ func TestReadingBlameOutput(t *testing.T) {
 | 
				
			|||||||
			commit, err := repo.GetCommit(c.CommitID)
 | 
								commit, err := repo.GetCommit(c.CommitID)
 | 
				
			||||||
			assert.NoError(t, err)
 | 
								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.NoError(t, err)
 | 
				
			||||||
			assert.NotNil(t, blameReader)
 | 
								assert.NotNil(t, blameReader)
 | 
				
			||||||
			defer blameReader.Close()
 | 
								defer blameReader.Close()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Blob represents a Git object.
 | 
					// Blob represents a Git object.
 | 
				
			||||||
type Blob struct {
 | 
					type Blob struct {
 | 
				
			||||||
	ID SHA1
 | 
						ID ObjectID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gogitEncodedObj plumbing.EncodedObject
 | 
						gogitEncodedObj plumbing.EncodedObject
 | 
				
			||||||
	name            string
 | 
						name            string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Blob represents a Git object.
 | 
					// Blob represents a Git object.
 | 
				
			||||||
type Blob struct {
 | 
					type Blob struct {
 | 
				
			||||||
	ID SHA1
 | 
						ID ObjectID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gotSize bool
 | 
						gotSize bool
 | 
				
			||||||
	size    int64
 | 
						size    int64
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,13 +21,13 @@ import (
 | 
				
			|||||||
// Commit represents a git commit.
 | 
					// Commit represents a git commit.
 | 
				
			||||||
type Commit struct {
 | 
					type Commit struct {
 | 
				
			||||||
	Tree
 | 
						Tree
 | 
				
			||||||
	ID            SHA1 // The ID of this commit object
 | 
						ID            ObjectID // The ID of this commit object
 | 
				
			||||||
	Author        *Signature
 | 
						Author        *Signature
 | 
				
			||||||
	Committer     *Signature
 | 
						Committer     *Signature
 | 
				
			||||||
	CommitMessage string
 | 
						CommitMessage string
 | 
				
			||||||
	Signature     *CommitGPGSignature
 | 
						Signature     *CommitGPGSignature
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Parents        []SHA1 // SHA1 strings
 | 
						Parents        []ObjectID // ID strings
 | 
				
			||||||
	submoduleCache *ObjectCache
 | 
						submoduleCache *ObjectCache
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -50,9 +50,9 @@ func (c *Commit) Summary() string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ParentID returns oid of n-th parent (0-based index).
 | 
					// ParentID returns oid of n-th parent (0-based index).
 | 
				
			||||||
// It returns nil if no such parent exists.
 | 
					// 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) {
 | 
						if n >= len(c.Parents) {
 | 
				
			||||||
		return SHA1{}, ErrNotExist{"", ""}
 | 
							return nil, ErrNotExist{"", ""}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return c.Parents[n], nil
 | 
						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
 | 
					// 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()
 | 
						this := c.ID.String()
 | 
				
			||||||
	that := commitHash.String()
 | 
						that := objectID.String()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if this == that {
 | 
						if this == that {
 | 
				
			||||||
		return false, nil
 | 
							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
 | 
					// IsForcePush returns true if a push from oldCommitHash to this is a force push
 | 
				
			||||||
func (c *Commit) IsForcePush(oldCommitID string) (bool, error) {
 | 
					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
 | 
							return false, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oldCommit, err := c.repo.GetCommit(oldCommitID)
 | 
						oldCommit, err := c.repo.GetCommit(oldCommitID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return false, err
 | 
							return false, err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,11 +59,11 @@ func convertPGPSignature(c *object.Commit) *CommitGPGSignature {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func convertCommit(c *object.Commit) *Commit {
 | 
					func convertCommit(c *object.Commit) *Commit {
 | 
				
			||||||
	return &Commit{
 | 
						return &Commit{
 | 
				
			||||||
		ID:            c.Hash,
 | 
							ID:            ParseGogitHash(c.Hash),
 | 
				
			||||||
		CommitMessage: c.Message,
 | 
							CommitMessage: c.Message,
 | 
				
			||||||
		Committer:     &c.Committer,
 | 
							Committer:     &c.Committer,
 | 
				
			||||||
		Author:        &c.Author,
 | 
							Author:        &c.Author,
 | 
				
			||||||
		Signature:     convertPGPSignature(c),
 | 
							Signature:     convertPGPSignature(c),
 | 
				
			||||||
		Parents:       c.ParentHashes,
 | 
							Parents:       ParseGogitHashArray(c.ParentHashes),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
 | 
				
			|||||||
		defer commitGraphFile.Close()
 | 
							defer commitGraphFile.Close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c, err := commitNodeIndex.Get(commit.ID)
 | 
						c, err := commitNodeIndex.Get(plumbing.Hash(commit.ID.RawValue()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -153,7 +153,7 @@ func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string,
 | 
				
			|||||||
		if typ != "commit" {
 | 
							if typ != "commit" {
 | 
				
			||||||
			return nil, fmt.Errorf("unexpected type: %s for commit id: %s", typ, commitID)
 | 
								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 {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,9 +14,9 @@ import (
 | 
				
			|||||||
// We need this to interpret commits from cat-file or cat-file --batch
 | 
					// 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
 | 
					// 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{
 | 
						commit := &Commit{
 | 
				
			||||||
		ID:        sha,
 | 
							ID:        objectID,
 | 
				
			||||||
		Author:    &Signature{},
 | 
							Author:    &Signature{},
 | 
				
			||||||
		Committer: &Signature{},
 | 
							Committer: &Signature{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -71,10 +71,10 @@ readLoop:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			switch string(split[0]) {
 | 
								switch string(split[0]) {
 | 
				
			||||||
			case "tree":
 | 
								case "tree":
 | 
				
			||||||
				commit.Tree = *NewTree(gitRepo, MustIDFromString(string(data)))
 | 
									commit.Tree = *NewTree(gitRepo, objectID.Type().MustIDFromString(string(data)))
 | 
				
			||||||
				_, _ = payloadSB.Write(line)
 | 
									_, _ = payloadSB.Write(line)
 | 
				
			||||||
			case "parent":
 | 
								case "parent":
 | 
				
			||||||
				commit.Parents = append(commit.Parents, MustIDFromString(string(data)))
 | 
									commit.Parents = append(commit.Parents, objectID.Type().MustIDFromString(string(data)))
 | 
				
			||||||
				_, _ = payloadSB.Write(line)
 | 
									_, _ = payloadSB.Write(line)
 | 
				
			||||||
			case "author":
 | 
								case "author":
 | 
				
			||||||
				commit.Author = &Signature{}
 | 
									commit.Author = &Signature{}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ gpgsig -----BEGIN PGP SIGNATURE-----
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
empty commit`
 | 
					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"))
 | 
						gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare"))
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NotNil(t, gitRepo)
 | 
						assert.NotNil(t, gitRepo)
 | 
				
			||||||
@@ -135,8 +135,8 @@ func TestHasPreviousCommit(t *testing.T) {
 | 
				
			|||||||
	commit, err := repo.GetCommit("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0")
 | 
						commit, err := repo.GetCommit("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0")
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	parentSHA := MustIDFromString("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2")
 | 
						parentSHA := repo.objectFormat.MustIDFromString("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2")
 | 
				
			||||||
	notParentSHA := MustIDFromString("2839944139e0de9737a044f78b0e4b40d989a9e3")
 | 
						notParentSHA := repo.objectFormat.MustIDFromString("2839944139e0de9737a044f78b0e4b40d989a9e3")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	haz, err := commit.HasPreviousCommit(parentSHA)
 | 
						haz, err := commit.HasPreviousCommit(parentSHA)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
					// GetCommitByPath gets the last commit for the entry in the provided commit
 | 
				
			||||||
func (c *LastCommitCache) GetCommitByPath(commitID, entryPath string) (*Commit, error) {
 | 
					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 {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lastCommit, err := c.Get(sha1.String(), entryPath)
 | 
						lastCommit, err := c.Get(sha.String(), entryPath)
 | 
				
			||||||
	if err != nil || lastCommit != nil {
 | 
						if err != nil || lastCommit != nil {
 | 
				
			||||||
		return lastCommit, err
 | 
							return lastCommit, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lastCommit, err = c.repo.getCommitByPathWithID(sha1, entryPath)
 | 
						lastCommit, err = c.repo.getCommitByPathWithID(sha, entryPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ package git
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
 | 
						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()
 | 
						commitNodeIndex, _ := c.repo.CommitNodeIndex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	index, err := commitNodeIndex.Get(c.ID)
 | 
						index, err := commitNodeIndex.Get(plumbing.Hash(c.ID.RawValue()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,17 +143,20 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Our "line" must look like: <commitid> SP (<parent> SP) * NUL
 | 
						// Our "line" must look like: <commitid> SP (<parent> SP) * NUL
 | 
				
			||||||
	ret.CommitID = string(g.next[0:40])
 | 
						commitIds := string(g.next)
 | 
				
			||||||
	parents := string(g.next[41:])
 | 
					 | 
				
			||||||
	if g.buffull {
 | 
						if g.buffull {
 | 
				
			||||||
		more, err := g.rd.ReadString('\x00')
 | 
							more, err := g.rd.ReadString('\x00')
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								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"
 | 
						// now read the next "line"
 | 
				
			||||||
	g.buffull = false
 | 
						g.buffull = false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	"github.com/go-git/go-git/v5/plumbing/object"
 | 
						"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()
 | 
							defer commitGraphFile.Close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	commitNode, err := commitNodeIndex.Get(notes.ID)
 | 
						commitNode, err := commitNodeIndex.Get(plumbing.Hash(notes.ID.RawValue()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										103
									
								
								modules/git/object_format.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								modules/git/object_format.go
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										143
									
								
								modules/git/object_id.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								modules/git/object_id.go
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								modules/git/object_id_gogit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								modules/git/object_id_gogit.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					//go:build gogit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/hash"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ParseGogitHash(h plumbing.Hash) ObjectID {
 | 
				
			||||||
 | 
						switch hash.Size {
 | 
				
			||||||
 | 
						case 20:
 | 
				
			||||||
 | 
							return ObjectFormatFromID(Sha1).MustID(h[:])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ParseGogitHashArray(objectIDs []plumbing.Hash) []ObjectID {
 | 
				
			||||||
 | 
						ret := make([]ObjectID, len(objectIDs))
 | 
				
			||||||
 | 
						for i, h := range objectIDs {
 | 
				
			||||||
 | 
							ret[i] = ParseGogitHash(h)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								modules/git/object_id_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								modules/git/object_id_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -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"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,12 +11,14 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"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/filemode"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/hash"
 | 
				
			||||||
	"github.com/go-git/go-git/v5/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseTreeEntries parses the output of a `git ls-tree -l` command.
 | 
					// 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)
 | 
						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]))
 | 
								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))
 | 
								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 {
 | 
							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 = plumbing.Hash(entry.ID.RawValue())
 | 
				
			||||||
		entry.gogitTreeEntry.Hash = id
 | 
					 | 
				
			||||||
		pos += 41 // skip over sha and trailing space
 | 
							pos += 41 // skip over sha and trailing space
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		end := pos + bytes.IndexByte(data[pos:], '\t')
 | 
							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).
 | 
							// In case entry name is surrounded by double quotes(it happens only in git-shell).
 | 
				
			||||||
		if data[pos] == '"' {
 | 
							if data[pos] == '"' {
 | 
				
			||||||
 | 
								var err error
 | 
				
			||||||
			entry.gogitTreeEntry.Name, err = strconv.Unquote(string(data[pos:end]))
 | 
								entry.gogitTreeEntry.Name, err = strconv.Unquote(string(data[pos:end]))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, fmt.Errorf("Invalid ls-tree output: %w", err)
 | 
									return nil, fmt.Errorf("Invalid ls-tree output: %w", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,8 +6,10 @@
 | 
				
			|||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"testing"
 | 
						"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/filemode"
 | 
				
			||||||
	"github.com/go-git/go-git/v5/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -26,9 +28,9 @@ func TestParseTreeEntries(t *testing.T) {
 | 
				
			|||||||
			Input: "100644 blob 61ab7345a1a3bbc590068ccae37b8515cfc5843c    1022\texample/file2.txt\n",
 | 
								Input: "100644 blob 61ab7345a1a3bbc590068ccae37b8515cfc5843c    1022\texample/file2.txt\n",
 | 
				
			||||||
			Expected: []*TreeEntry{
 | 
								Expected: []*TreeEntry{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
 | 
										ID: ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
 | 
				
			||||||
					gogitTreeEntry: &object.TreeEntry{
 | 
										gogitTreeEntry: &object.TreeEntry{
 | 
				
			||||||
						Hash: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
 | 
											Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()),
 | 
				
			||||||
						Name: "example/file2.txt",
 | 
											Name: "example/file2.txt",
 | 
				
			||||||
						Mode: filemode.Regular,
 | 
											Mode: filemode.Regular,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
@@ -42,9 +44,9 @@ func TestParseTreeEntries(t *testing.T) {
 | 
				
			|||||||
				"040000 tree 1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8       -\texample\n",
 | 
									"040000 tree 1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8       -\texample\n",
 | 
				
			||||||
			Expected: []*TreeEntry{
 | 
								Expected: []*TreeEntry{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
 | 
										ID: ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
 | 
				
			||||||
					gogitTreeEntry: &object.TreeEntry{
 | 
										gogitTreeEntry: &object.TreeEntry{
 | 
				
			||||||
						Hash: MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c"),
 | 
											Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("61ab7345a1a3bbc590068ccae37b8515cfc5843c").RawValue()),
 | 
				
			||||||
						Name: "example/\n.txt",
 | 
											Name: "example/\n.txt",
 | 
				
			||||||
						Mode: filemode.Symlink,
 | 
											Mode: filemode.Symlink,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
@@ -52,10 +54,10 @@ func TestParseTreeEntries(t *testing.T) {
 | 
				
			|||||||
					sized: true,
 | 
										sized: true,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID:    MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"),
 | 
										ID:    ObjectFormatFromID(Sha1).MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"),
 | 
				
			||||||
					sized: true,
 | 
										sized: true,
 | 
				
			||||||
					gogitTreeEntry: &object.TreeEntry{
 | 
										gogitTreeEntry: &object.TreeEntry{
 | 
				
			||||||
						Hash: MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8"),
 | 
											Hash: plumbing.Hash(ObjectFormatFromID(Sha1).MustIDFromString("1d01fb729fb0db5881daaa6030f9f2d3cd3d5ae8").RawValue()),
 | 
				
			||||||
						Name: "example",
 | 
											Name: "example",
 | 
				
			||||||
						Mode: filemode.Dir,
 | 
											Mode: filemode.Dir,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
@@ -65,8 +67,12 @@ func TestParseTreeEntries(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, testCase := range testCases {
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
		entries, err := ParseTreeEntries([]byte(testCase.Input))
 | 
							entries, err := ParseTreeEntries(ObjectFormatFromID(Sha1), []byte(testCase.Input))
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							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)
 | 
							assert.EqualValues(t, testCase.Expected, entries)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,13 +17,13 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseTreeEntries parses the output of a `git ls-tree -l` command.
 | 
					// ParseTreeEntries parses the output of a `git ls-tree -l` command.
 | 
				
			||||||
func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
 | 
					func ParseTreeEntries(objectFormat ObjectFormat, data []byte) ([]*TreeEntry, error) {
 | 
				
			||||||
	return parseTreeEntries(data, nil)
 | 
						return parseTreeEntries(objectFormat, data, nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var sepSpace = []byte{' '}
 | 
					var sepSpace = []byte{' '}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
 | 
					func parseTreeEntries(objectFormat ObjectFormat, data []byte, ptree *Tree) ([]*TreeEntry, error) {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1)
 | 
						entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1)
 | 
				
			||||||
	for pos := 0; pos < len(data); {
 | 
						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))
 | 
								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 {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("invalid ls-tree output (invalid object id): %q, err: %w", line, err)
 | 
								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
 | 
						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)
 | 
						fnameBuf := make([]byte, 4096)
 | 
				
			||||||
	modeBuf := make([]byte, 40)
 | 
						modeBuf := make([]byte, 40)
 | 
				
			||||||
	shaBuf := make([]byte, 40)
 | 
						shaBuf := make([]byte, objectFormat.FullLength())
 | 
				
			||||||
	entries := make([]*TreeEntry, 0, 10)
 | 
						entries := make([]*TreeEntry, 0, 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
loop:
 | 
					loop:
 | 
				
			||||||
	for sz > 0 {
 | 
						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 != nil {
 | 
				
			||||||
			if err == io.EOF {
 | 
								if err == io.EOF {
 | 
				
			||||||
				break loop
 | 
									break loop
 | 
				
			||||||
@@ -127,7 +127,7 @@ loop:
 | 
				
			|||||||
			return nil, fmt.Errorf("unknown mode: %v", string(mode))
 | 
								return nil, fmt.Errorf("unknown mode: %v", string(mode))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		entry.ID = MustID(sha)
 | 
							entry.ID = objectFormat.MustID(sha)
 | 
				
			||||||
		entry.name = string(fname)
 | 
							entry.name = string(fname)
 | 
				
			||||||
		entries = append(entries, entry)
 | 
							entries = append(entries, entry)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestParseTreeEntriesLong(t *testing.T) {
 | 
					func TestParseTreeEntriesLong(t *testing.T) {
 | 
				
			||||||
 | 
						objectFormat := ObjectFormatFromID(Sha1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		Input    string
 | 
							Input    string
 | 
				
			||||||
		Expected []*TreeEntry
 | 
							Expected []*TreeEntry
 | 
				
			||||||
@@ -24,28 +26,28 @@ func TestParseTreeEntriesLong(t *testing.T) {
 | 
				
			|||||||
`,
 | 
					`,
 | 
				
			||||||
			Expected: []*TreeEntry{
 | 
								Expected: []*TreeEntry{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID:        MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
 | 
										ID:        objectFormat.MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
 | 
				
			||||||
					name:      "README.md",
 | 
										name:      "README.md",
 | 
				
			||||||
					entryMode: EntryModeBlob,
 | 
										entryMode: EntryModeBlob,
 | 
				
			||||||
					size:      8218,
 | 
										size:      8218,
 | 
				
			||||||
					sized:     true,
 | 
										sized:     true,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID:        MustIDFromString("037f27dc9d353ae4fd50f0474b2194c593914e35"),
 | 
										ID:        objectFormat.MustIDFromString("037f27dc9d353ae4fd50f0474b2194c593914e35"),
 | 
				
			||||||
					name:      "README_ZH.md",
 | 
										name:      "README_ZH.md",
 | 
				
			||||||
					entryMode: EntryModeBlob,
 | 
										entryMode: EntryModeBlob,
 | 
				
			||||||
					size:      4681,
 | 
										size:      4681,
 | 
				
			||||||
					sized:     true,
 | 
										sized:     true,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID:        MustIDFromString("9846a94f7e8350a916632929d0fda38c90dd2ca8"),
 | 
										ID:        objectFormat.MustIDFromString("9846a94f7e8350a916632929d0fda38c90dd2ca8"),
 | 
				
			||||||
					name:      "SECURITY.md",
 | 
										name:      "SECURITY.md",
 | 
				
			||||||
					entryMode: EntryModeBlob,
 | 
										entryMode: EntryModeBlob,
 | 
				
			||||||
					size:      429,
 | 
										size:      429,
 | 
				
			||||||
					sized:     true,
 | 
										sized:     true,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID:        MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
 | 
										ID:        objectFormat.MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
 | 
				
			||||||
					name:      "assets",
 | 
										name:      "assets",
 | 
				
			||||||
					entryMode: EntryModeTree,
 | 
										entryMode: EntryModeTree,
 | 
				
			||||||
					sized:     true,
 | 
										sized:     true,
 | 
				
			||||||
@@ -54,7 +56,7 @@ func TestParseTreeEntriesLong(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, testCase := range testCases {
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
		entries, err := ParseTreeEntries([]byte(testCase.Input))
 | 
							entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input))
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Len(t, entries, len(testCase.Expected))
 | 
							assert.Len(t, entries, len(testCase.Expected))
 | 
				
			||||||
		for i, entry := range entries {
 | 
							for i, entry := range entries {
 | 
				
			||||||
@@ -64,6 +66,8 @@ func TestParseTreeEntriesLong(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestParseTreeEntriesShort(t *testing.T) {
 | 
					func TestParseTreeEntriesShort(t *testing.T) {
 | 
				
			||||||
 | 
						objectFormat := ObjectFormatFromID(Sha1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		Input    string
 | 
							Input    string
 | 
				
			||||||
		Expected []*TreeEntry
 | 
							Expected []*TreeEntry
 | 
				
			||||||
@@ -74,12 +78,12 @@ func TestParseTreeEntriesShort(t *testing.T) {
 | 
				
			|||||||
`,
 | 
					`,
 | 
				
			||||||
			Expected: []*TreeEntry{
 | 
								Expected: []*TreeEntry{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID:        MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
 | 
										ID:        objectFormat.MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
 | 
				
			||||||
					name:      "README.md",
 | 
										name:      "README.md",
 | 
				
			||||||
					entryMode: EntryModeBlob,
 | 
										entryMode: EntryModeBlob,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					ID:        MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
 | 
										ID:        objectFormat.MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
 | 
				
			||||||
					name:      "assets",
 | 
										name:      "assets",
 | 
				
			||||||
					entryMode: EntryModeTree,
 | 
										entryMode: EntryModeTree,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -87,7 +91,7 @@ func TestParseTreeEntriesShort(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, testCase := range testCases {
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
		entries, err := ParseTreeEntries([]byte(testCase.Input))
 | 
							entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input))
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Len(t, entries, len(testCase.Expected))
 | 
							assert.Len(t, entries, len(testCase.Expected))
 | 
				
			||||||
		for i, entry := range entries {
 | 
							for i, entry := range entries {
 | 
				
			||||||
@@ -98,7 +102,7 @@ func TestParseTreeEntriesShort(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestParseTreeEntriesInvalid(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
 | 
						// 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.Error(t, err)
 | 
				
			||||||
	assert.Len(t, entries, 0)
 | 
						assert.Len(t, entries, 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gogit "github.com/go-git/go-git/v5"
 | 
						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"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,7 +27,7 @@ type LFSResult struct {
 | 
				
			|||||||
	SHA            string
 | 
						SHA            string
 | 
				
			||||||
	Summary        string
 | 
						Summary        string
 | 
				
			||||||
	When           time.Time
 | 
						When           time.Time
 | 
				
			||||||
	ParentHashes   []git.SHA1
 | 
						ParentHashes   []git.ObjectID
 | 
				
			||||||
	BranchName     string
 | 
						BranchName     string
 | 
				
			||||||
	FullCommitName 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) }
 | 
					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
 | 
					// 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{}
 | 
						resultsMap := map[string]*LFSResult{}
 | 
				
			||||||
	results := make([]*LFSResult, 0)
 | 
						results := make([]*LFSResult, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,13 +66,18 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
 | 
				
			|||||||
			if err == io.EOF {
 | 
								if err == io.EOF {
 | 
				
			||||||
				break
 | 
									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{
 | 
									result := LFSResult{
 | 
				
			||||||
					Name:         name,
 | 
										Name:         name,
 | 
				
			||||||
					SHA:          gitCommit.Hash.String(),
 | 
										SHA:          gitCommit.Hash.String(),
 | 
				
			||||||
					Summary:      strings.Split(strings.TrimSpace(gitCommit.Message), "\n")[0],
 | 
										Summary:      strings.Split(strings.TrimSpace(gitCommit.Message), "\n")[0],
 | 
				
			||||||
					When:         gitCommit.Author.When,
 | 
										When:         gitCommit.Author.When,
 | 
				
			||||||
					ParentHashes: gitCommit.ParentHashes,
 | 
										ParentHashes: parents,
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				resultsMap[gitCommit.Hash.String()+":"+name] = &result
 | 
									resultsMap[gitCommit.Hash.String()+":"+name] = &result
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ type LFSResult struct {
 | 
				
			|||||||
	SHA            string
 | 
						SHA            string
 | 
				
			||||||
	Summary        string
 | 
						Summary        string
 | 
				
			||||||
	When           time.Time
 | 
						When           time.Time
 | 
				
			||||||
	ParentHashes   []git.SHA1
 | 
						ParentIDs      []git.ObjectID
 | 
				
			||||||
	BranchName     string
 | 
						BranchName     string
 | 
				
			||||||
	FullCommitName 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) }
 | 
					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
 | 
					// 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{}
 | 
						resultsMap := map[string]*LFSResult{}
 | 
				
			||||||
	results := make([]*LFSResult, 0)
 | 
						results := make([]*LFSResult, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,7 +75,7 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	fnameBuf := make([]byte, 4096)
 | 
						fnameBuf := make([]byte, 4096)
 | 
				
			||||||
	modeBuf := make([]byte, 40)
 | 
						modeBuf := make([]byte, 40)
 | 
				
			||||||
	workingShaBuf := make([]byte, 20)
 | 
						workingShaBuf := make([]byte, objectID.Type().FullLength()/2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for scan.Scan() {
 | 
						for scan.Scan() {
 | 
				
			||||||
		// Get the next commit ID
 | 
							// Get the next commit ID
 | 
				
			||||||
@@ -115,7 +115,11 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			case "commit":
 | 
								case "commit":
 | 
				
			||||||
				// Read in the commit to get its tree and in case this is one of the last used commits
 | 
									// 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 {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, err
 | 
										return nil, err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -123,32 +127,31 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
 | 
				
			|||||||
					return nil, err
 | 
										return nil, err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				_, err := batchStdinWriter.Write([]byte(curCommit.Tree.ID.String() + "\n"))
 | 
									if _, err := batchStdinWriter.Write([]byte(curCommit.Tree.ID.String() + "\n")); err != nil {
 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					return nil, err
 | 
										return nil, err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				curPath = ""
 | 
									curPath = ""
 | 
				
			||||||
			case "tree":
 | 
								case "tree":
 | 
				
			||||||
				var n int64
 | 
									var n int64
 | 
				
			||||||
				for n < size {
 | 
									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 {
 | 
										if err != nil {
 | 
				
			||||||
						return nil, err
 | 
											return nil, err
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					n += int64(count)
 | 
										n += int64(count)
 | 
				
			||||||
					if bytes.Equal(sha20byte, hash[:]) {
 | 
										if bytes.Equal(binObjectID, objectID.RawValue()) {
 | 
				
			||||||
						result := LFSResult{
 | 
											result := LFSResult{
 | 
				
			||||||
							Name:         curPath + string(fname),
 | 
												Name:      curPath + string(fname),
 | 
				
			||||||
							SHA:          curCommit.ID.String(),
 | 
												SHA:       curCommit.ID.String(),
 | 
				
			||||||
							Summary:      strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
 | 
												Summary:   strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
 | 
				
			||||||
							When:         curCommit.Author.When,
 | 
												When:      curCommit.Author.When,
 | 
				
			||||||
							ParentHashes: curCommit.Parents,
 | 
												ParentIDs: curCommit.Parents,
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
						resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result
 | 
											resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result
 | 
				
			||||||
					} else if string(mode) == git.EntryModeTree.String() {
 | 
										} else if string(mode) == git.EntryModeTree.String() {
 | 
				
			||||||
						sha40Byte := make([]byte, 40)
 | 
											hexObjectID := make([]byte, objectID.Type().FullLength())
 | 
				
			||||||
						git.To40ByteSHA(sha20byte, sha40Byte)
 | 
											git.BinToHex(objectID.Type(), binObjectID, hexObjectID)
 | 
				
			||||||
						trees = append(trees, sha40Byte)
 | 
											trees = append(trees, hexObjectID)
 | 
				
			||||||
						paths = append(paths, curPath+string(fname)+"/")
 | 
											paths = append(paths, curPath+string(fname)+"/")
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -180,8 +183,8 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, result := range resultsMap {
 | 
						for _, result := range resultsMap {
 | 
				
			||||||
		hasParent := false
 | 
							hasParent := false
 | 
				
			||||||
		for _, parentHash := range result.ParentHashes {
 | 
							for _, parentID := range result.ParentIDs {
 | 
				
			||||||
			if _, hasParent = resultsMap[parentHash.String()+":"+result.Name]; hasParent {
 | 
								if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent {
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,7 @@ func SanitizeRefPattern(name string) string {
 | 
				
			|||||||
type Reference struct {
 | 
					type Reference struct {
 | 
				
			||||||
	Name   string
 | 
						Name   string
 | 
				
			||||||
	repo   *Repository
 | 
						repo   *Repository
 | 
				
			||||||
	Object SHA1 // The id of this commit object
 | 
						Object ObjectID // The id of this commit object
 | 
				
			||||||
	Type   string
 | 
						Type   string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -205,7 +205,7 @@ func RefURL(repoURL, ref string) string {
 | 
				
			|||||||
		return repoURL + "/src/branch/" + refName
 | 
							return repoURL + "/src/branch/" + refName
 | 
				
			||||||
	case refFullName.IsTag():
 | 
						case refFullName.IsTag():
 | 
				
			||||||
		return repoURL + "/src/tag/" + refName
 | 
							return repoURL + "/src/tag/" + refName
 | 
				
			||||||
	case !IsValidSHAPattern(ref):
 | 
						case !ObjectFormatFromID(Sha1).IsValid(ref):
 | 
				
			||||||
		// assume they mean a branch
 | 
							// assume they mean a branch
 | 
				
			||||||
		return repoURL + "/src/branch/" + refName
 | 
							return repoURL + "/src/branch/" + refName
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package git
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
@@ -62,14 +63,40 @@ func IsRepoURLAccessible(ctx context.Context, url string) bool {
 | 
				
			|||||||
	return err == nil
 | 
						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.
 | 
					// 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)
 | 
						err := os.MkdirAll(repoPath, os.ModePerm)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd := NewCommand(ctx, "init")
 | 
						cmd := NewCommand(ctx, "init", "--object-format").AddDynamicArguments(objectFormat.String())
 | 
				
			||||||
	if bare {
 | 
						if bare {
 | 
				
			||||||
		cmd.AddArguments("--bare")
 | 
							cmd.AddArguments("--bare")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/go-git/go-billy/v5/osfs"
 | 
						"github.com/go-git/go-billy/v5/osfs"
 | 
				
			||||||
	gogit "github.com/go-git/go-git/v5"
 | 
						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/plumbing/cache"
 | 
				
			||||||
	"github.com/go-git/go-git/v5/storage/filesystem"
 | 
						"github.com/go-git/go-git/v5/storage/filesystem"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -32,6 +33,7 @@ type Repository struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	Ctx             context.Context
 | 
						Ctx             context.Context
 | 
				
			||||||
	LastCommitCache *LastCommitCache
 | 
						LastCommitCache *LastCommitCache
 | 
				
			||||||
 | 
						objectFormat    ObjectFormat
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
 | 
					// 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,
 | 
							gogitStorage: storage,
 | 
				
			||||||
		tagCache:     newObjectCache(),
 | 
							tagCache:     newObjectCache(),
 | 
				
			||||||
		Ctx:          ctx,
 | 
							Ctx:          ctx,
 | 
				
			||||||
 | 
							objectFormat: ParseGogitHash(plumbing.ZeroHash).Type(),
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,8 @@ type Repository struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	Ctx             context.Context
 | 
						Ctx             context.Context
 | 
				
			||||||
	LastCommitCache *LastCommitCache
 | 
						LastCommitCache *LastCommitCache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						objectFormat ObjectFormat
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
 | 
					// 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.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath)
 | 
				
			||||||
	repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(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
 | 
						return repo, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ package git
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetBlob finds the blob object in the repository.
 | 
					// GetBlob finds the blob object in the repository.
 | 
				
			||||||
func (repo *Repository) GetBlob(idStr string) (*Blob, error) {
 | 
					func (repo *Repository) GetBlob(idStr string) (*Blob, error) {
 | 
				
			||||||
	id, err := NewIDFromString(idStr)
 | 
						id, err := repo.objectFormat.NewIDFromString(idStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,8 @@ import (
 | 
				
			|||||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) getBlob(id SHA1) (*Blob, error) {
 | 
					func (repo *Repository) getBlob(id ObjectID) (*Blob, error) {
 | 
				
			||||||
	encodedObj, err := repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, id)
 | 
						encodedObj, err := repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, plumbing.Hash(id.RawValue()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, ErrNotExist{id.String(), ""}
 | 
							return nil, ErrNotExist{id.String(), ""}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) getBlob(id SHA1) (*Blob, error) {
 | 
					func (repo *Repository) getBlob(id ObjectID) (*Blob, error) {
 | 
				
			||||||
	if id.IsZero() {
 | 
						if id.IsZero() {
 | 
				
			||||||
		return nil, ErrNotExist{id.String(), ""}
 | 
							return nil, ErrNotExist{id.String(), ""}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,7 +61,7 @@ func TestRepository_GetBlob_NoId(t *testing.T) {
 | 
				
			|||||||
	defer r.Close()
 | 
						defer r.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testCase := ""
 | 
						testCase := ""
 | 
				
			||||||
	testError := fmt.Errorf("Length must be 40: %s", testCase)
 | 
						testError := fmt.Errorf("length must be 40: %s", testCase)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	blob, err := r.GetBlob(testCase)
 | 
						blob, err := r.GetBlob(testCase)
 | 
				
			||||||
	assert.Nil(t, blob)
 | 
						assert.Nil(t, blob)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,8 +6,6 @@ package git
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/hex"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@@ -28,7 +26,7 @@ func (repo *Repository) GetTagCommitID(name string) (string, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetCommit returns commit object of by ID string.
 | 
					// GetCommit returns commit object of by ID string.
 | 
				
			||||||
func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
 | 
					func (repo *Repository) GetCommit(commitID string) (*Commit, error) {
 | 
				
			||||||
	id, err := repo.ConvertToSHA1(commitID)
 | 
						id, err := repo.ConvertToGitID(commitID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -54,7 +52,7 @@ func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
 | 
				
			|||||||
	return repo.GetCommit(commitID)
 | 
						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.
 | 
						// File name starts with ':' must be escaped.
 | 
				
			||||||
	if relpath[0] == ':' {
 | 
						if relpath[0] == ':' {
 | 
				
			||||||
		relpath = `\` + relpath
 | 
							relpath = `\` + relpath
 | 
				
			||||||
@@ -65,7 +63,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
 | 
				
			|||||||
		return nil, runErr
 | 
							return nil, runErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id, err := NewIDFromString(stdout)
 | 
						id, err := repo.objectFormat.NewIDFromString(stdout)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -90,7 +88,7 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
 | 
				
			|||||||
	return commits[0], nil
 | 
						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").
 | 
						cmd := NewCommand(repo.Ctx, "log").
 | 
				
			||||||
		AddOptionFormat("--skip=%d", (page-1)*pageSize).
 | 
							AddOptionFormat("--skip=%d", (page-1)*pageSize).
 | 
				
			||||||
		AddOptionFormat("--max-count=%d", 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)
 | 
						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
 | 
						// add common arguments to git command
 | 
				
			||||||
	addCommonSearchArgs := func(c *Command) {
 | 
						addCommonSearchArgs := func(c *Command) {
 | 
				
			||||||
		// ignore case
 | 
							// ignore case
 | 
				
			||||||
@@ -164,7 +162,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
 | 
				
			|||||||
	// then let's iterate over them
 | 
						// then let's iterate over them
 | 
				
			||||||
	for _, v := range opts.Keywords {
 | 
						for _, v := range opts.Keywords {
 | 
				
			||||||
		// ignore anything not matching a valid sha pattern
 | 
							// 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
 | 
								// create new git log command with 1 commit limit
 | 
				
			||||||
			hashCmd := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat)
 | 
								hashCmd := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat)
 | 
				
			||||||
			// add previous arguments except for --grep and --all
 | 
								// add previous arguments except for --grep and --all
 | 
				
			||||||
@@ -245,25 +243,22 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						len := repo.objectFormat.FullLength()
 | 
				
			||||||
	commits := []*Commit{}
 | 
						commits := []*Commit{}
 | 
				
			||||||
	shaline := [41]byte{}
 | 
						shaline := make([]byte, len+1)
 | 
				
			||||||
	var sha1 SHA1
 | 
					 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		n, err := io.ReadFull(stdoutReader, shaline[:])
 | 
							n, err := io.ReadFull(stdoutReader, shaline)
 | 
				
			||||||
		if err != nil || n < 40 {
 | 
							if err != nil || n < len {
 | 
				
			||||||
			if err == io.EOF {
 | 
								if err == io.EOF {
 | 
				
			||||||
				err = nil
 | 
									err = nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return commits, err
 | 
								return commits, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		n, err = hex.Decode(sha1[:], shaline[0:40])
 | 
							objectID, err := repo.objectFormat.NewIDFromString(string(shaline[0:len]))
 | 
				
			||||||
		if n != 20 {
 | 
					 | 
				
			||||||
			err = fmt.Errorf("invalid sha %q", string(shaline[:40]))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		commit, err := repo.getCommit(sha1)
 | 
							commit, err := repo.getCommit(objectID)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								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.
 | 
					// 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)
 | 
						cmd := NewCommand(repo.Ctx, "log", prettyLogFormat)
 | 
				
			||||||
	if limit > 0 {
 | 
						if limit > 0 {
 | 
				
			||||||
		cmd.AddOptionFormat("-%d", limit)
 | 
							cmd.AddOptionFormat("-%d", limit)
 | 
				
			||||||
@@ -426,11 +421,11 @@ func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) {
 | 
				
			|||||||
	return commits, nil
 | 
						return commits, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) getCommitsBefore(id SHA1) ([]*Commit, error) {
 | 
					func (repo *Repository) getCommitsBefore(id ObjectID) ([]*Commit, error) {
 | 
				
			||||||
	return repo.commitsBefore(id, 0)
 | 
						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)
 | 
						return repo.commitsBefore(id, num)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
						"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"
 | 
						"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))
 | 
						return repo.gogitRepo.Storer.RemoveReference(plumbing.ReferenceName(name))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConvertToSHA1 returns a Hash object from a potential ID string
 | 
					// ConvertToHash returns a Hash object from a potential ID string
 | 
				
			||||||
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
 | 
					func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) {
 | 
				
			||||||
	if len(commitID) == SHAFullLength {
 | 
						objectFormat := repo.objectFormat
 | 
				
			||||||
		sha1, err := NewIDFromString(commitID)
 | 
						if len(commitID) == hash.HexSize && objectFormat.IsValid(commitID) {
 | 
				
			||||||
 | 
							ID, err := objectFormat.NewIDFromString(commitID)
 | 
				
			||||||
		if err == nil {
 | 
							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, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(&RunOpts{Dir: repo.Path})
 | 
				
			||||||
 | 
						actualCommitID = strings.TrimSpace(actualCommitID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if strings.Contains(err.Error(), "unknown revision or path") ||
 | 
							if strings.Contains(err.Error(), "unknown revision or path") ||
 | 
				
			||||||
			strings.Contains(err.Error(), "fatal: Needed a single revision") {
 | 
								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.
 | 
					// IsCommitExist returns true if given commit exists in current repository.
 | 
				
			||||||
func (repo *Repository) IsCommitExist(name string) bool {
 | 
					func (repo *Repository) IsCommitExist(name string) bool {
 | 
				
			||||||
	hash := plumbing.NewHash(name)
 | 
						hash, err := repo.ConvertToGitID(name)
 | 
				
			||||||
	_, err := repo.gogitRepo.CommitObject(hash)
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = repo.gogitRepo.CommitObject(plumbing.Hash(hash.RawValue()))
 | 
				
			||||||
	return err == nil
 | 
						return err == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
 | 
					func (repo *Repository) getCommit(id ObjectID) (*Commit, error) {
 | 
				
			||||||
	var tagObject *object.Tag
 | 
						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 {
 | 
						if err == plumbing.ErrObjectNotFound {
 | 
				
			||||||
		tagObject, err = repo.gogitRepo.TagObject(id)
 | 
							tagObject, err = repo.gogitRepo.TagObject(commitID)
 | 
				
			||||||
		if err == plumbing.ErrObjectNotFound {
 | 
							if err == plumbing.ErrObjectNotFound {
 | 
				
			||||||
			return nil, ErrNotExist{
 | 
								return nil, ErrNotExist{
 | 
				
			||||||
				ID: id.String(),
 | 
									ID: id.String(),
 | 
				
			||||||
@@ -94,7 +101,7 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	commit.Tree.ID = tree.Hash
 | 
						commit.Tree.ID = ParseGogitHash(tree.Hash)
 | 
				
			||||||
	commit.Tree.gogitTree = tree
 | 
						commit.Tree.gogitTree = tree
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return commit, nil
 | 
						return commit, nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,7 +65,7 @@ func (repo *Repository) IsCommitExist(name string) bool {
 | 
				
			|||||||
	return err == nil
 | 
						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)
 | 
						wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,7 +74,7 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
 | 
				
			|||||||
	return repo.getCommitFromBatchReader(rd, id)
 | 
						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)
 | 
						_, typ, size, err := ReadBatchLine(rd)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if errors.Is(err, io.EOF) || IsErrNotExist(err) {
 | 
							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 {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		tag, err := parseTagData(data)
 | 
							tag, err := parseTagData(id.Type(), data)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								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
 | 
					// ConvertToGitID returns a GitHash object from a potential ID string
 | 
				
			||||||
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
 | 
					func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) {
 | 
				
			||||||
	if len(commitID) == SHAFullLength && IsValidSHAPattern(commitID) {
 | 
						IDType := repo.objectFormat
 | 
				
			||||||
		sha1, err := NewIDFromString(commitID)
 | 
						if len(commitID) == IDType.FullLength() && IDType.IsValid(commitID) {
 | 
				
			||||||
 | 
							ID, err := repo.objectFormat.NewIDFromString(commitID)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			return sha1, nil
 | 
								return ID, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -144,15 +145,15 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
 | 
				
			|||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
	_, err := wr.Write([]byte(commitID + "\n"))
 | 
						_, err := wr.Write([]byte(commitID + "\n"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return SHA1{}, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sha, _, _, err := ReadBatchLine(rd)
 | 
						sha, _, _, err := ReadBatchLine(rd)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if IsErrNotExist(err) {
 | 
							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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
					// 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) {
 | 
					func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) {
 | 
				
			||||||
	cmd := NewCommand(repo.Ctx, "diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z")
 | 
						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)
 | 
							cmd.AddDynamicArguments(head)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cmd.AddDynamicArguments(base, head)
 | 
							cmd.AddDynamicArguments(base, head)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -131,12 +131,12 @@ func TestGetCommitFilesChanged(t *testing.T) {
 | 
				
			|||||||
		files      []string
 | 
							files      []string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			EmptySHA,
 | 
								repo.objectFormat.Empty().String(),
 | 
				
			||||||
			"95bb4d39648ee7e325106df01a621c530863a653",
 | 
								"95bb4d39648ee7e325106df01a621c530863a653",
 | 
				
			||||||
			[]string{"file1.txt"},
 | 
								[]string{"file1.txt"},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			EmptySHA,
 | 
								repo.objectFormat.Empty().String(),
 | 
				
			||||||
			"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
 | 
								"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
 | 
				
			||||||
			[]string{"file2.txt"},
 | 
								[]string{"file2.txt"},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -146,7 +146,7 @@ func TestGetCommitFilesChanged(t *testing.T) {
 | 
				
			|||||||
			[]string{"file2.txt"},
 | 
								[]string{"file2.txt"},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			EmptyTreeSHA,
 | 
								repo.objectFormat.EmptyTree().String(),
 | 
				
			||||||
			"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
 | 
								"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
 | 
				
			||||||
			[]string{"file1.txt", "file2.txt"},
 | 
								[]string{"file1.txt", "file2.txt"},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ func (gpgSettings *GPGSettings) LoadPublicKeyContent() error {
 | 
				
			|||||||
		"gpg -a --export",
 | 
							"gpg -a --export",
 | 
				
			||||||
		"gpg", "-a", "--export", gpgSettings.KeyID)
 | 
							"gpg", "-a", "--export", gpgSettings.KeyID)
 | 
				
			||||||
	if err != nil {
 | 
						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
 | 
						gpgSettings.PublicKeyContent = content
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,12 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ReadTreeToIndex reads a treeish to the index
 | 
					// ReadTreeToIndex reads a treeish to the index
 | 
				
			||||||
func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
 | 
					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})
 | 
							res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(&RunOpts{Dir: repo.Path})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
@@ -25,14 +30,14 @@ func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string)
 | 
				
			|||||||
			treeish = res[:len(res)-1]
 | 
								treeish = res[:len(res)-1]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	id, err := NewIDFromString(treeish)
 | 
						id, err := objectFormat.NewIDFromString(treeish)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return repo.readTreeToIndex(id, indexFilename...)
 | 
						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
 | 
						var env []string
 | 
				
			||||||
	if len(indexFilename) > 0 {
 | 
						if len(indexFilename) > 0 {
 | 
				
			||||||
		env = append(os.Environ(), "GIT_INDEX_FILE="+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)
 | 
						buffer := new(bytes.Buffer)
 | 
				
			||||||
	for _, file := range filenames {
 | 
						for _, file := range filenames {
 | 
				
			||||||
		if file != "" {
 | 
							if file != "" {
 | 
				
			||||||
			buffer.WriteString("0 0000000000000000000000000000000000000000\t")
 | 
								buffer.WriteString("0 ")
 | 
				
			||||||
 | 
								buffer.WriteString(repo.objectFormat.Empty().String())
 | 
				
			||||||
 | 
								buffer.WriteByte('\t')
 | 
				
			||||||
			buffer.WriteString(file)
 | 
								buffer.WriteString(file)
 | 
				
			||||||
			buffer.WriteByte('\000')
 | 
								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
 | 
					// 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)
 | 
						cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, object.String(), filename)
 | 
				
			||||||
	_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
 | 
						_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
@@ -121,7 +128,7 @@ func (repo *Repository) WriteTree() (*Tree, error) {
 | 
				
			|||||||
	if runErr != nil {
 | 
						if runErr != nil {
 | 
				
			||||||
		return nil, runErr
 | 
							return nil, runErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	id, err := NewIDFromString(strings.TrimSpace(stdout))
 | 
						id, err := repo.objectFormat.NewIDFromString(strings.TrimSpace(stdout))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
 | 
				
			|||||||
		return nil, ErrNotExist{commitID, ""}
 | 
							return nil, ErrNotExist{commitID, ""}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sha, err := NewIDFromString(string(shaBytes))
 | 
						sha, err := repo.objectFormat.NewIDFromString(string(shaBytes))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Debug("Unable to get commit for: %s. Err: %v", commitID, err)
 | 
							log.Debug("Unable to get commit for: %s. Err: %v", commitID, err)
 | 
				
			||||||
		return nil, ErrNotExist{commitID, ""}
 | 
							return nil, ErrNotExist{commitID, ""}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,17 +31,47 @@ func (o ObjectType) Bytes() []byte {
 | 
				
			|||||||
	return []byte(o)
 | 
						return []byte(o)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HashObject takes a reader and returns SHA1 hash for that reader
 | 
					type EmptyReader struct{}
 | 
				
			||||||
func (repo *Repository) HashObject(reader io.Reader) (SHA1, error) {
 | 
					
 | 
				
			||||||
	idStr, err := repo.hashObject(reader)
 | 
					func (EmptyReader) Read(p []byte) (int, error) {
 | 
				
			||||||
	if err != nil {
 | 
						return 0, io.EOF
 | 
				
			||||||
		return SHA1{}, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return NewIDFromString(idStr)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) hashObject(reader io.Reader) (string, error) {
 | 
					func (repo *Repository) GetObjectFormat() (ObjectFormat, error) {
 | 
				
			||||||
	cmd := NewCommand(repo.Ctx, "hash-object", "-w", "--stdin")
 | 
						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)
 | 
						stdout := new(bytes.Buffer)
 | 
				
			||||||
	stderr := new(bytes.Buffer)
 | 
						stderr := new(bytes.Buffer)
 | 
				
			||||||
	err := cmd.Run(&RunOpts{
 | 
						err := cmd.Run(&RunOpts{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,13 +30,13 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
 | 
				
			|||||||
			refType := string(ObjectCommit)
 | 
								refType := string(ObjectCommit)
 | 
				
			||||||
			if ref.Name().IsTag() {
 | 
								if ref.Name().IsTag() {
 | 
				
			||||||
				// tags can be of type `commit` (lightweight) or `tag` (annotated)
 | 
									// 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
 | 
										refType = tagType
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			r := &Reference{
 | 
								r := &Reference{
 | 
				
			||||||
				Name:   ref.Name().String(),
 | 
									Name:   ref.Name().String(),
 | 
				
			||||||
				Object: ref.Hash(),
 | 
									Object: ParseGogitHash(ref.Hash()),
 | 
				
			||||||
				Type:   refType,
 | 
									Type:   refType,
 | 
				
			||||||
				repo:   repo,
 | 
									repo:   repo,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,7 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
 | 
				
			|||||||
		if pattern == "" || strings.HasPrefix(refName, pattern) {
 | 
							if pattern == "" || strings.HasPrefix(refName, pattern) {
 | 
				
			||||||
			r := &Reference{
 | 
								r := &Reference{
 | 
				
			||||||
				Name:   refName,
 | 
									Name:   refName,
 | 
				
			||||||
				Object: MustIDFromString(sha),
 | 
									Object: repo.objectFormat.MustIDFromString(sha),
 | 
				
			||||||
				Type:   typ,
 | 
									Type:   typ,
 | 
				
			||||||
				repo:   repo,
 | 
									repo:   repo,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id, err := NewIDFromString(idStr)
 | 
						id, err := repo.objectFormat.NewIDFromString(idStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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
 | 
					// GetTagWithID returns a Git tag by given name and ID
 | 
				
			||||||
func (repo *Repository) GetTagWithID(idStr, name string) (*Tag, error) {
 | 
					func (repo *Repository) GetTagWithID(idStr, name string) (*Tag, error) {
 | 
				
			||||||
	id, err := NewIDFromString(idStr)
 | 
						id, err := repo.objectFormat.NewIDFromString(idStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -139,7 +139,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
 | 
				
			|||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tag, err := parseTagRef(ref)
 | 
							tag, err := parseTagRef(repo.objectFormat, ref)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, 0, fmt.Errorf("GetTagInfos: parse tag: %w", err)
 | 
								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.
 | 
					// 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{
 | 
						tag = &Tag{
 | 
				
			||||||
		Type: ref["objecttype"],
 | 
							Type: ref["objecttype"],
 | 
				
			||||||
		Name: ref["refname:short"],
 | 
							Name: ref["refname:short"],
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tag.ID, err = NewIDFromString(ref["objectname"])
 | 
						tag.ID, err = objectFormat.NewIDFromString(ref["objectname"])
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("parse objectname '%s': %w", ref["objectname"], err)
 | 
							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
 | 
							tag.Object = tag.ID
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		// annotated tag
 | 
							// annotated tag
 | 
				
			||||||
		tag.Object, err = NewIDFromString(ref["object"])
 | 
							tag.Object, err = objectFormat.NewIDFromString(ref["object"])
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("parse object '%s': %w", ref["object"], err)
 | 
								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
 | 
					// GetAnnotatedTag returns a Git tag by its SHA, must be an annotated tag
 | 
				
			||||||
func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
 | 
					func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
 | 
				
			||||||
	id, err := NewIDFromString(sha)
 | 
						id, err := repo.objectFormat.NewIDFromString(sha)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
					// 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
 | 
						// 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 != nil {
 | 
				
			||||||
		if err == plumbing.ErrReferenceNotFound {
 | 
							if err == plumbing.ErrReferenceNotFound {
 | 
				
			||||||
			return "", &ErrNotExist{ID: id.String()}
 | 
								return "", &ErrNotExist{ID: id.String()}
 | 
				
			||||||
@@ -68,7 +68,7 @@ func (repo *Repository) GetTagType(id SHA1) (string, error) {
 | 
				
			|||||||
	return obj.Type().String(), nil
 | 
						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())
 | 
						t, ok := repo.tagCache.Get(tagID.String())
 | 
				
			||||||
	if ok {
 | 
						if ok {
 | 
				
			||||||
		log.Debug("Hit cache: %s", tagID)
 | 
							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
 | 
							// every tag should have a commit ID so return all errors
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	commitID, err := NewIDFromString(commitIDStr)
 | 
						commitID, err := IDFromString(commitIDStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -112,7 +112,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
 | 
				
			|||||||
		return tag, nil
 | 
							return tag, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gogitTag, err := repo.gogitRepo.TagObject(tagID)
 | 
						gogitTag, err := repo.gogitRepo.TagObject(plumbing.Hash(tagID.RawValue()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if err == plumbing.ErrReferenceNotFound {
 | 
							if err == plumbing.ErrReferenceNotFound {
 | 
				
			||||||
			return nil, &ErrNotExist{ID: tagID.String()}
 | 
								return nil, &ErrNotExist{ID: tagID.String()}
 | 
				
			||||||
@@ -124,7 +124,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
 | 
				
			|||||||
	tag := &Tag{
 | 
						tag := &Tag{
 | 
				
			||||||
		Name:    name,
 | 
							Name:    name,
 | 
				
			||||||
		ID:      tagID,
 | 
							ID:      tagID,
 | 
				
			||||||
		Object:  gogitTag.Target,
 | 
							Object:  commitID.Type().MustID(gogitTag.Target[:]),
 | 
				
			||||||
		Type:    tp,
 | 
							Type:    tp,
 | 
				
			||||||
		Tagger:  &gogitTag.Tagger,
 | 
							Tagger:  &gogitTag.Tagger,
 | 
				
			||||||
		Message: gogitTag.Message,
 | 
							Message: gogitTag.Message,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
					// 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)
 | 
						wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
	_, err := wr.Write([]byte(id.String() + "\n"))
 | 
						_, err := wr.Write([]byte(id.String() + "\n"))
 | 
				
			||||||
@@ -44,7 +44,7 @@ func (repo *Repository) GetTagType(id SHA1) (string, error) {
 | 
				
			|||||||
	return typ, nil
 | 
						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())
 | 
						t, ok := repo.tagCache.Get(tagID.String())
 | 
				
			||||||
	if ok {
 | 
						if ok {
 | 
				
			||||||
		log.Debug("Hit cache: %s", tagID)
 | 
							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
 | 
							// every tag should have a commit ID so return all errors
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	commitID, err := NewIDFromString(commitIDStr)
 | 
						commitID, err := repo.objectFormat.NewIDFromString(commitIDStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -117,7 +117,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tag, err := parseTagData(data)
 | 
						tag, err := parseTagData(tagID.Type(), data)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -194,6 +194,7 @@ func TestRepository_GetAnnotatedTag(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRepository_parseTagRef(t *testing.T) {
 | 
					func TestRepository_parseTagRef(t *testing.T) {
 | 
				
			||||||
 | 
						sha1 := ObjectFormatFromID(Sha1)
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		name string
 | 
							name string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -223,8 +224,8 @@ func TestRepository_parseTagRef(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			want: &Tag{
 | 
								want: &Tag{
 | 
				
			||||||
				Name:      "v1.9.1",
 | 
									Name:      "v1.9.1",
 | 
				
			||||||
				ID:        MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
 | 
									ID:        sha1.MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
 | 
				
			||||||
				Object:    MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
 | 
									Object:    sha1.MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"),
 | 
				
			||||||
				Type:      "commit",
 | 
									Type:      "commit",
 | 
				
			||||||
				Tagger:    parseAuthorLine(t, "Foo Bar <foo@bar.com> 1565789218 +0300"),
 | 
									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",
 | 
									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{
 | 
								want: &Tag{
 | 
				
			||||||
				Name:      "v0.0.1",
 | 
									Name:      "v0.0.1",
 | 
				
			||||||
				ID:        MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
 | 
									ID:        sha1.MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
 | 
				
			||||||
				Object:    MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
 | 
									Object:    sha1.MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
 | 
				
			||||||
				Type:      "tag",
 | 
									Type:      "tag",
 | 
				
			||||||
				Tagger:    parseAuthorLine(t, "Foo Bar <foo@bar.com> 1565789218 +0300"),
 | 
									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",
 | 
									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{
 | 
								want: &Tag{
 | 
				
			||||||
				Name:    "v0.0.1",
 | 
									Name:    "v0.0.1",
 | 
				
			||||||
				ID:      MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
 | 
									ID:      sha1.MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"),
 | 
				
			||||||
				Object:  MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
 | 
									Object:  sha1.MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"),
 | 
				
			||||||
				Type:    "tag",
 | 
									Type:    "tag",
 | 
				
			||||||
				Tagger:  parseAuthorLine(t, "Foo Bar <foo@bar.com> 1565789218 +0300"),
 | 
									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",
 | 
									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 {
 | 
						for _, test := range tests {
 | 
				
			||||||
		tc := test // don't close over loop variable
 | 
							tc := test // don't close over loop variable
 | 
				
			||||||
		t.Run(tc.name, func(t *testing.T) {
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
			got, err := parseTagRef(tc.givenRef)
 | 
								got, err := parseTagRef(sha1, tc.givenRef)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if tc.wantErr {
 | 
								if tc.wantErr {
 | 
				
			||||||
				require.Error(t, err)
 | 
									require.Error(t, err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ type CommitTreeOpts struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommitTree creates a commit from a given tree id for the user with provided message
 | 
					// 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)
 | 
						commitTimeStr := time.Now().Format(time.RFC3339)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Because this may call hooks we should pass in the environment
 | 
						// 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,
 | 
							Stderr: stderr,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						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()))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,8 +6,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) getTree(id SHA1) (*Tree, error) {
 | 
					import "github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	gogitTree, err := repo.gogitRepo.TreeObject(id)
 | 
					
 | 
				
			||||||
 | 
					func (repo *Repository) getTree(id ObjectID) (*Tree, error) {
 | 
				
			||||||
 | 
						gogitTree, err := repo.gogitRepo.TreeObject(plumbing.Hash(id.RawValue()))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -19,7 +21,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetTree find the tree object in the repository.
 | 
					// GetTree find the tree object in the repository.
 | 
				
			||||||
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
 | 
					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})
 | 
							res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
@@ -28,14 +30,14 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
 | 
				
			|||||||
			idStr = res[:len(res)-1]
 | 
								idStr = res[:len(res)-1]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	id, err := NewIDFromString(idStr)
 | 
						id, err := repo.objectFormat.NewIDFromString(idStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	resolvedID := id
 | 
						resolvedID := id
 | 
				
			||||||
	commitObject, err := repo.gogitRepo.CommitObject(id)
 | 
						commitObject, err := repo.gogitRepo.CommitObject(plumbing.Hash(id.RawValue()))
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		id = SHA1(commitObject.TreeHash)
 | 
							id = ParseGogitHash(commitObject.TreeHash)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	treeObject, err := repo.getTree(id)
 | 
						treeObject, err := repo.getTree(id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) getTree(id SHA1) (*Tree, error) {
 | 
					func (repo *Repository) getTree(id ObjectID) (*Tree, error) {
 | 
				
			||||||
	wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
 | 
						wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,7 +28,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		tag, err := parseTagData(data)
 | 
							tag, err := parseTagData(id.Type(), data)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -51,7 +51,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
 | 
				
			|||||||
	case "tree":
 | 
						case "tree":
 | 
				
			||||||
		tree := NewTree(repo, id)
 | 
							tree := NewTree(repo, id)
 | 
				
			||||||
		tree.ResolvedID = id
 | 
							tree.ResolvedID = id
 | 
				
			||||||
		tree.entries, err = catBatchParseTreeEntries(tree, rd, size)
 | 
							tree.entries, err = catBatchParseTreeEntries(repo.objectFormat, tree, rd, size)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -66,7 +66,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetTree find the tree object in the repository.
 | 
					// GetTree find the tree object in the repository.
 | 
				
			||||||
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
 | 
					func (repo *Repository) GetTree(idStr string) (*Tree, error) {
 | 
				
			||||||
	if len(idStr) != SHAFullLength {
 | 
						if len(idStr) != repo.objectFormat.FullLength() {
 | 
				
			||||||
		res, err := repo.GetRefCommitID(idStr)
 | 
							res, err := repo.GetRefCommitID(idStr)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
@@ -75,7 +75,7 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
 | 
				
			|||||||
			idStr = res
 | 
								idStr = res
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	id, err := NewIDFromString(idStr)
 | 
						id, err := repo.objectFormat.NewIDFromString(idStr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -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)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -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"))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -17,8 +17,8 @@ const (
 | 
				
			|||||||
// Tag represents a Git tag.
 | 
					// Tag represents a Git tag.
 | 
				
			||||||
type Tag struct {
 | 
					type Tag struct {
 | 
				
			||||||
	Name      string
 | 
						Name      string
 | 
				
			||||||
	ID        SHA1
 | 
						ID        ObjectID
 | 
				
			||||||
	Object    SHA1 // The id of this commit object
 | 
						Object    ObjectID // The id of this commit object
 | 
				
			||||||
	Type      string
 | 
						Type      string
 | 
				
			||||||
	Tagger    *Signature
 | 
						Tagger    *Signature
 | 
				
			||||||
	Message   string
 | 
						Message   string
 | 
				
			||||||
@@ -33,8 +33,10 @@ func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) {
 | 
				
			|||||||
// Parse commit information from the (uncompressed) raw
 | 
					// Parse commit information from the (uncompressed) raw
 | 
				
			||||||
// data from the commit object.
 | 
					// data from the commit object.
 | 
				
			||||||
// \n\n separate headers from message
 | 
					// \n\n separate headers from message
 | 
				
			||||||
func parseTagData(data []byte) (*Tag, error) {
 | 
					func parseTagData(objectFormat ObjectFormat, data []byte) (*Tag, error) {
 | 
				
			||||||
	tag := new(Tag)
 | 
						tag := new(Tag)
 | 
				
			||||||
 | 
						tag.ID = objectFormat.NewEmptyID()
 | 
				
			||||||
 | 
						tag.Object = objectFormat.NewEmptyID()
 | 
				
			||||||
	tag.Tagger = &Signature{}
 | 
						tag.Tagger = &Signature{}
 | 
				
			||||||
	// we now have the contents of the commit object. Let's investigate...
 | 
						// we now have the contents of the commit object. Let's investigate...
 | 
				
			||||||
	nextline := 0
 | 
						nextline := 0
 | 
				
			||||||
@@ -48,7 +50,7 @@ l:
 | 
				
			|||||||
			reftype := line[:spacepos]
 | 
								reftype := line[:spacepos]
 | 
				
			||||||
			switch string(reftype) {
 | 
								switch string(reftype) {
 | 
				
			||||||
			case "object":
 | 
								case "object":
 | 
				
			||||||
				id, err := NewIDFromString(string(line[spacepos+1:]))
 | 
									id, err := objectFormat.NewIDFromString(string(line[spacepos+1:]))
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return nil, err
 | 
										return nil, err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,8 +22,8 @@ tagger Lucas Michot <lucas@semalead.com> 1484491741 +0100
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
`), tag: Tag{
 | 
					`), tag: Tag{
 | 
				
			||||||
			Name:      "",
 | 
								Name:      "",
 | 
				
			||||||
			ID:        SHA1{},
 | 
								ID:        NewSha1(),
 | 
				
			||||||
			Object:    SHA1{0x3b, 0x11, 0x4a, 0xb8, 0x0, 0xc6, 0x43, 0x2a, 0xd4, 0x23, 0x87, 0xcc, 0xf6, 0xbc, 0x8d, 0x43, 0x88, 0xa2, 0x88, 0x5a},
 | 
								Object:    &Sha1Hash{0x3b, 0x11, 0x4a, 0xb8, 0x0, 0xc6, 0x43, 0x2a, 0xd4, 0x23, 0x87, 0xcc, 0xf6, 0xbc, 0x8d, 0x43, 0x88, 0xa2, 0x88, 0x5a},
 | 
				
			||||||
			Type:      "commit",
 | 
								Type:      "commit",
 | 
				
			||||||
			Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484491741, 0)},
 | 
								Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484491741, 0)},
 | 
				
			||||||
			Message:   "",
 | 
								Message:   "",
 | 
				
			||||||
@@ -39,8 +39,8 @@ o
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
ono`), tag: Tag{
 | 
					ono`), tag: Tag{
 | 
				
			||||||
			Name:      "",
 | 
								Name:      "",
 | 
				
			||||||
			ID:        SHA1{},
 | 
								ID:        NewSha1(),
 | 
				
			||||||
			Object:    SHA1{0x7c, 0xdf, 0x42, 0xc0, 0xb1, 0xcc, 0x76, 0x3a, 0xb7, 0xe4, 0xc3, 0x3c, 0x47, 0xa2, 0x4e, 0x27, 0xc6, 0x6b, 0xfc, 0xcc},
 | 
								Object:    &Sha1Hash{0x7c, 0xdf, 0x42, 0xc0, 0xb1, 0xcc, 0x76, 0x3a, 0xb7, 0xe4, 0xc3, 0x3c, 0x47, 0xa2, 0x4e, 0x27, 0xc6, 0x6b, 0xfc, 0xcc},
 | 
				
			||||||
			Type:      "commit",
 | 
								Type:      "commit",
 | 
				
			||||||
			Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484553735, 0)},
 | 
								Tagger:    &Signature{Name: "Lucas Michot", Email: "lucas@semalead.com", When: time.Unix(1484553735, 0)},
 | 
				
			||||||
			Message:   "test message\no\n\nono",
 | 
								Message:   "test message\no\n\nono",
 | 
				
			||||||
@@ -49,7 +49,7 @@ ono`), tag: Tag{
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range testData {
 | 
						for _, test := range testData {
 | 
				
			||||||
		tag, err := parseTagData(test.data)
 | 
							tag, err := parseTagData(ObjectFormatFromID(Sha1), test.data)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.EqualValues(t, test.tag.ID, tag.ID)
 | 
							assert.EqualValues(t, test.tag.ID, tag.ID)
 | 
				
			||||||
		assert.EqualValues(t, test.tag.Object, tag.Object)
 | 
							assert.EqualValues(t, test.tag.Object, tag.Object)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTree create a new tree according the repository and tree id
 | 
					// 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{
 | 
						return &Tree{
 | 
				
			||||||
		ID:   id,
 | 
							ID:   id,
 | 
				
			||||||
		repo: repo,
 | 
							repo: repo,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
 | 
				
			|||||||
			gogitTreeEntry: &object.TreeEntry{
 | 
								gogitTreeEntry: &object.TreeEntry{
 | 
				
			||||||
				Name: "",
 | 
									Name: "",
 | 
				
			||||||
				Mode: filemode.Dir,
 | 
									Mode: filemode.Dir,
 | 
				
			||||||
				Hash: t.ID,
 | 
									Hash: plumbing.Hash(t.ID.RawValue()),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}, nil
 | 
							}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TreeEntry the leaf in the git tree
 | 
					// TreeEntry the leaf in the git tree
 | 
				
			||||||
type TreeEntry struct {
 | 
					type TreeEntry struct {
 | 
				
			||||||
	ID SHA1
 | 
						ID ObjectID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gogitTreeEntry *object.TreeEntry
 | 
						gogitTreeEntry *object.TreeEntry
 | 
				
			||||||
	ptree          *Tree
 | 
						ptree          *Tree
 | 
				
			||||||
@@ -88,7 +88,7 @@ func (te *TreeEntry) Blob() *Blob {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &Blob{
 | 
						return &Blob{
 | 
				
			||||||
		ID:              te.gogitTreeEntry.Hash,
 | 
							ID:              ParseGogitHash(te.gogitTreeEntry.Hash),
 | 
				
			||||||
		gogitEncodedObj: encodedObj,
 | 
							gogitEncodedObj: encodedObj,
 | 
				
			||||||
		name:            te.Name(),
 | 
							name:            te.Name(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ import "code.gitea.io/gitea/modules/log"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TreeEntry the leaf in the git tree
 | 
					// TreeEntry the leaf in the git tree
 | 
				
			||||||
type TreeEntry struct {
 | 
					type TreeEntry struct {
 | 
				
			||||||
	ID SHA1
 | 
						ID ObjectID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ptree *Tree
 | 
						ptree *Tree
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,8 +15,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Tree represents a flat directory listing.
 | 
					// Tree represents a flat directory listing.
 | 
				
			||||||
type Tree struct {
 | 
					type Tree struct {
 | 
				
			||||||
	ID         SHA1
 | 
						ID         ObjectID
 | 
				
			||||||
	ResolvedID SHA1
 | 
						ResolvedID ObjectID
 | 
				
			||||||
	repo       *Repository
 | 
						repo       *Repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gogitTree *object.Tree
 | 
						gogitTree *object.Tree
 | 
				
			||||||
@@ -26,7 +26,7 @@ type Tree struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *Tree) loadTreeObject() error {
 | 
					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 {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -47,7 +47,7 @@ func (t *Tree) ListEntries() (Entries, error) {
 | 
				
			|||||||
	entries := make([]*TreeEntry, len(t.gogitTree.Entries))
 | 
						entries := make([]*TreeEntry, len(t.gogitTree.Entries))
 | 
				
			||||||
	for i, entry := range t.gogitTree.Entries {
 | 
						for i, entry := range t.gogitTree.Entries {
 | 
				
			||||||
		entries[i] = &TreeEntry{
 | 
							entries[i] = &TreeEntry{
 | 
				
			||||||
			ID:             entry.Hash,
 | 
								ID:             ParseGogitHash(entry.Hash),
 | 
				
			||||||
			gogitTreeEntry: &t.gogitTree.Entries[i],
 | 
								gogitTreeEntry: &t.gogitTree.Entries[i],
 | 
				
			||||||
			ptree:          t,
 | 
								ptree:          t,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -81,7 +81,7 @@ func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		convertedEntry := &TreeEntry{
 | 
							convertedEntry := &TreeEntry{
 | 
				
			||||||
			ID:             entry.Hash,
 | 
								ID:             ParseGogitHash(entry.Hash),
 | 
				
			||||||
			gogitTreeEntry: &entry,
 | 
								gogitTreeEntry: &entry,
 | 
				
			||||||
			ptree:          t,
 | 
								ptree:          t,
 | 
				
			||||||
			fullName:       fullName,
 | 
								fullName:       fullName,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,8 +13,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Tree represents a flat directory listing.
 | 
					// Tree represents a flat directory listing.
 | 
				
			||||||
type Tree struct {
 | 
					type Tree struct {
 | 
				
			||||||
	ID         SHA1
 | 
						ID         ObjectID
 | 
				
			||||||
	ResolvedID SHA1
 | 
						ResolvedID ObjectID
 | 
				
			||||||
	repo       *Repository
 | 
						repo       *Repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// parent tree
 | 
						// parent tree
 | 
				
			||||||
@@ -54,7 +54,7 @@ func (t *Tree) ListEntries() (Entries, error) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if typ == "tree" {
 | 
							if typ == "tree" {
 | 
				
			||||||
			t.entries, err = catBatchParseTreeEntries(t, rd, sz)
 | 
								t.entries, err = catBatchParseTreeEntries(t.ID.Type(), t, rd, sz)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -90,7 +90,7 @@ func (t *Tree) ListEntries() (Entries, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	t.entries, err = parseTreeEntries(stdout, t)
 | 
						t.entries, err = parseTreeEntries(t.repo.objectFormat, stdout, t)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		t.entriesParsed = true
 | 
							t.entriesParsed = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -114,7 +114,7 @@ func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	t.entriesRecursive, err = parseTreeEntries(stdout, t)
 | 
						t.entriesRecursive, err = parseTreeEntries(t.repo.objectFormat, stdout, t)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		t.entriesRecursiveParsed = true
 | 
							t.entriesRecursiveParsed = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,8 +62,8 @@ func isIndexable(entry *git.TreeEntry) bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command
 | 
					// parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command
 | 
				
			||||||
func parseGitLsTreeOutput(stdout []byte) ([]internal.FileUpdate, error) {
 | 
					func parseGitLsTreeOutput(objectFormat git.ObjectFormat, stdout []byte) ([]internal.FileUpdate, error) {
 | 
				
			||||||
	entries, err := git.ParseTreeEntries(stdout)
 | 
						entries, err := git.ParseTreeEntries(objectFormat, stdout)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -92,7 +92,11 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						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
 | 
						return &changes, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -169,6 +173,11 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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
 | 
						return &changes, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,7 +144,7 @@ func TestCommitToPushCommit(t *testing.T) {
 | 
				
			|||||||
		When:  now,
 | 
							When:  now,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	const hexString = "0123456789abcdef0123456789abcdef01234567"
 | 
						const hexString = "0123456789abcdef0123456789abcdef01234567"
 | 
				
			||||||
	sha1, err := git.NewIDFromString(hexString)
 | 
						sha1, err := git.IDFromString(hexString)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	pushCommit := CommitToPushCommit(&git.Commit{
 | 
						pushCommit := CommitToPushCommit(&git.Commit{
 | 
				
			||||||
		ID:            sha1,
 | 
							ID:            sha1,
 | 
				
			||||||
@@ -169,11 +169,12 @@ func TestListToPushCommits(t *testing.T) {
 | 
				
			|||||||
		When:  now,
 | 
							When:  now,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hashType := git.ObjectFormatFromID(git.Sha1)
 | 
				
			||||||
	const hexString1 = "0123456789abcdef0123456789abcdef01234567"
 | 
						const hexString1 = "0123456789abcdef0123456789abcdef01234567"
 | 
				
			||||||
	hash1, err := git.NewIDFromString(hexString1)
 | 
						hash1, err := hashType.NewIDFromString(hexString1)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	const hexString2 = "fedcba9876543210fedcba9876543210fedcba98"
 | 
						const hexString2 = "fedcba9876543210fedcba9876543210fedcba98"
 | 
				
			||||||
	hash2, err := git.NewIDFromString(hexString2)
 | 
						hash2, err := hashType.NewIDFromString(hexString2)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	l := []*git.Commit{
 | 
						l := []*git.Commit{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
							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
 | 
							return generateRepo, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -188,7 +188,7 @@ func InitRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
 | 
				
			|||||||
	return nil
 | 
						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.
 | 
						// Somehow the directory could exist.
 | 
				
			||||||
	repoPath := repo_model.RepoPath(owner, name)
 | 
						repoPath := repo_model.RepoPath(owner, name)
 | 
				
			||||||
	isExist, err := util.IsExist(repoPath)
 | 
						isExist, err := util.IsExist(repoPath)
 | 
				
			||||||
@@ -204,7 +204,7 @@ func CheckInitRepository(ctx context.Context, owner, name string) (err error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Init git bare new repository.
 | 
						// 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)
 | 
							return fmt.Errorf("git.InitRepository: %w", err)
 | 
				
			||||||
	} else if err = CreateDelegateHooks(repoPath); err != nil {
 | 
						} else if err = CreateDelegateHooks(repoPath); err != nil {
 | 
				
			||||||
		return fmt.Errorf("createDelegateHooks: %w", err)
 | 
							return fmt.Errorf("createDelegateHooks: %w", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,12 +20,14 @@ type PushUpdateOptions struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsNewRef return true if it's a first-time push to a branch, tag or etc.
 | 
					// IsNewRef return true if it's a first-time push to a branch, tag or etc.
 | 
				
			||||||
func (opts *PushUpdateOptions) IsNewRef() bool {
 | 
					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
 | 
					// IsDelRef return true if it's a deletion to a branch or tag
 | 
				
			||||||
func (opts *PushUpdateOptions) IsDelRef() bool {
 | 
					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
 | 
					// IsUpdateRef return true if it's an update operation
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,7 +66,7 @@ func getNote(ctx *context.APIContext, identifier string) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	commitSHA, err := ctx.Repo.GitRepo.ConvertToSHA1(identifier)
 | 
						commitID, err := ctx.Repo.GitRepo.ConvertToGitID(identifier)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if git.IsErrNotExist(err) {
 | 
							if git.IsErrNotExist(err) {
 | 
				
			||||||
			ctx.NotFound(err)
 | 
								ctx.NotFound(err)
 | 
				
			||||||
@@ -77,7 +77,7 @@ func getNote(ctx *context.APIContext, identifier string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var note git.Note
 | 
						var note git.Note
 | 
				
			||||||
	if err := git.GetNote(ctx, ctx.Repo.GitRepo, commitSHA.String(), ¬e); err != nil {
 | 
						if err := git.GetNote(ctx, ctx.Repo.GitRepo, commitID.String(), ¬e); err != nil {
 | 
				
			||||||
		if git.IsErrNotExist(err) {
 | 
							if git.IsErrNotExist(err) {
 | 
				
			||||||
			ctx.NotFound(identifier)
 | 
								ctx.NotFound(identifier)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -253,6 +253,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
 | 
				
			|||||||
		DefaultBranch: opt.DefaultBranch,
 | 
							DefaultBranch: opt.DefaultBranch,
 | 
				
			||||||
		TrustModel:    repo_model.ToTrustModel(opt.TrustModel),
 | 
							TrustModel:    repo_model.ToTrustModel(opt.TrustModel),
 | 
				
			||||||
		IsTemplate:    opt.Template,
 | 
							IsTemplate:    opt.Template,
 | 
				
			||||||
 | 
							ObjectFormat:  git.ObjectFormatFromID(git.Sha1),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if repo_model.IsErrRepoAlreadyExist(err) {
 | 
							if repo_model.IsErrRepoAlreadyExist(err) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,27 +69,28 @@ func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (str
 | 
				
			|||||||
	return "", "", nil
 | 
						return "", "", nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConvertToSHA1 returns a full-length SHA1 from a potential ID string
 | 
					// ConvertToObjectID returns a full-length SHA1 from a potential ID string
 | 
				
			||||||
func ConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) (git.SHA1, error) {
 | 
					func ConvertToObjectID(ctx gocontext.Context, repo *context.Repository, commitID string) (git.ObjectID, error) {
 | 
				
			||||||
	if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) {
 | 
						objectFormat, _ := repo.GitRepo.GetObjectFormat()
 | 
				
			||||||
		sha1, err := git.NewIDFromString(commitID)
 | 
						if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) {
 | 
				
			||||||
 | 
							sha, err := objectFormat.NewIDFromString(commitID)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			return sha1, nil
 | 
								return sha, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.Repository.RepoPath())
 | 
						gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.Repository.RepoPath())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
 | 
							return objectFormat.Empty(), fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer closer.Close()
 | 
						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
 | 
					// 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 {
 | 
					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 {
 | 
						if err != nil {
 | 
				
			||||||
		return commitID
 | 
							return commitID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -159,8 +159,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// If we've pushed a branch (and not deleted it)
 | 
							// 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
 | 
								// First ensure we have the repository loaded, we're allowed pulls requests and we can get the base repo
 | 
				
			||||||
			if repo == nil {
 | 
								if repo == nil {
 | 
				
			||||||
				repo = loadRepository(ctx, ownerName, repoName)
 | 
									repo = loadRepository(ctx, ownerName, repoName)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -145,8 +145,9 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	repo := ctx.Repo.Repository
 | 
						repo := ctx.Repo.Repository
 | 
				
			||||||
	gitRepo := ctx.Repo.GitRepo
 | 
						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)
 | 
							log.Warn("Forbidden: Branch: %s is the default branch in %-v and cannot be deleted", branchName, repo)
 | 
				
			||||||
		ctx.JSON(http.StatusForbidden, private.Response{
 | 
							ctx.JSON(http.StatusForbidden, private.Response{
 | 
				
			||||||
			UserMsg: fmt.Sprintf("branch %s is the default branch and cannot be deleted", branchName),
 | 
								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:
 | 
						// First of all we need to enforce absolutely:
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// 1. Detect and prevent deletion of the branch
 | 
						// 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)
 | 
							log.Warn("Forbidden: Branch: %s in %-v is protected from deletion", branchName, repo)
 | 
				
			||||||
		ctx.JSON(http.StatusForbidden, private.Response{
 | 
							ctx.JSON(http.StatusForbidden, private.Response{
 | 
				
			||||||
			UserMsg: fmt.Sprintf("branch %s is protected from deletion", branchName),
 | 
								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
 | 
						// 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})
 | 
							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 {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
 | 
								log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,8 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
 | 
				
			|||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var command *git.Command
 | 
						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":
 | 
							// 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
 | 
							// 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
 | 
							// 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()
 | 
							_ = stdoutReader.Close()
 | 
				
			||||||
		_ = stdoutWriter.Close()
 | 
							_ = stdoutWriter.Close()
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
	hash := git.MustIDFromString(sha)
 | 
						objectFormat, _ := repo.GetObjectFormat()
 | 
				
			||||||
 | 
						commitID := objectFormat.MustIDFromString(sha)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return git.NewCommand(repo.Ctx, "cat-file", "commit").AddDynamicArguments(sha).
 | 
						return git.NewCommand(repo.Ctx, "cat-file", "commit").AddDynamicArguments(sha).
 | 
				
			||||||
		Run(&git.RunOpts{
 | 
							Run(&git.RunOpts{
 | 
				
			||||||
@@ -91,7 +93,7 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
 | 
				
			|||||||
			Stdout: stdoutWriter,
 | 
								Stdout: stdoutWriter,
 | 
				
			||||||
			PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
 | 
								PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
 | 
				
			||||||
				_ = stdoutWriter.Close()
 | 
									_ = stdoutWriter.Close()
 | 
				
			||||||
				commit, err := git.CommitFromReader(repo, hash, stdoutReader)
 | 
									commit, err := git.CommitFromReader(repo, commitID, stdoutReader)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,14 +22,17 @@ func TestVerifyCommits(t *testing.T) {
 | 
				
			|||||||
	defer gitRepo.Close()
 | 
						defer gitRepo.Close()
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						objectFormat, err := gitRepo.GetObjectFormat()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		base, head string
 | 
							base, head string
 | 
				
			||||||
		verified   bool
 | 
							verified   bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"72920278f2f999e3005801e5d5b8ab8139d3641c", "d766f2917716d45be24bfa968b8409544941be32", true},
 | 
							{"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},
 | 
							{"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 {
 | 
						for _, tc := range testCases {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -131,7 +131,12 @@ type blameResult struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) {
 | 
					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 {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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 {
 | 
							if len(r.Parts) == 0 && r.UsesIgnoreRevs {
 | 
				
			||||||
			// try again without ignored revs
 | 
								// 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 {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -147,11 +147,18 @@ func RestoreBranchPost(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							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
 | 
						// Don't return error below this
 | 
				
			||||||
	if err := repo_service.PushUpdate(
 | 
						if err := repo_service.PushUpdate(
 | 
				
			||||||
		&repo_module.PushUpdateOptions{
 | 
							&repo_module.PushUpdateOptions{
 | 
				
			||||||
			RefFullName:  git.RefNameFromBranch(deletedBranch.Name),
 | 
								RefFullName:  git.RefNameFromBranch(deletedBranch.Name),
 | 
				
			||||||
			OldCommitID:  git.EmptySHA,
 | 
								OldCommitID:  objectFormat.Empty().String(),
 | 
				
			||||||
			NewCommitID:  deletedBranch.CommitID,
 | 
								NewCommitID:  deletedBranch.CommitID,
 | 
				
			||||||
			PusherID:     ctx.Doer.ID,
 | 
								PusherID:     ctx.Doer.ID,
 | 
				
			||||||
			PusherName:   ctx.Doer.Name,
 | 
								PusherName:   ctx.Doer.Name,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -294,7 +294,7 @@ func Diff(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(commitID) != git.SHAFullLength {
 | 
						if len(commitID) != commit.ID.Type().FullLength() {
 | 
				
			||||||
		commitID = commit.ID.String()
 | 
							commitID = commit.ID.String()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -310,13 +310,14 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
 | 
				
			|||||||
	baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch)
 | 
						baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch)
 | 
				
			||||||
	baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch)
 | 
						baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch)
 | 
				
			||||||
	baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
 | 
						baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch)
 | 
				
			||||||
 | 
						objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat()
 | 
				
			||||||
	if !baseIsCommit && !baseIsBranch && !baseIsTag {
 | 
						if !baseIsCommit && !baseIsBranch && !baseIsTag {
 | 
				
			||||||
		// Check if baseBranch is short sha commit hash
 | 
							// Check if baseBranch is short sha commit hash
 | 
				
			||||||
		if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil {
 | 
							if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil {
 | 
				
			||||||
			ci.BaseBranch = baseCommit.ID.String()
 | 
								ci.BaseBranch = baseCommit.ID.String()
 | 
				
			||||||
			ctx.Data["BaseBranch"] = ci.BaseBranch
 | 
								ctx.Data["BaseBranch"] = ci.BaseBranch
 | 
				
			||||||
			baseIsCommit = true
 | 
								baseIsCommit = true
 | 
				
			||||||
		} else if ci.BaseBranch == git.EmptySHA {
 | 
							} else if ci.BaseBranch == objectFormat.Empty().String() {
 | 
				
			||||||
			if isSameRepo {
 | 
								if isSameRepo {
 | 
				
			||||||
				ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch))
 | 
									ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
								log.Error("Failed to init bare repo for git-receive-pack cache: %v", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -159,6 +159,7 @@ func Create(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["private"] = getRepoPrivate(ctx)
 | 
						ctx.Data["private"] = getRepoPrivate(ctx)
 | 
				
			||||||
	ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
 | 
						ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
 | 
				
			||||||
	ctx.Data["default_branch"] = setting.Repository.DefaultBranch
 | 
						ctx.Data["default_branch"] = setting.Repository.DefaultBranch
 | 
				
			||||||
 | 
						ctx.Data["hash_type"] = "sha1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctxUser := checkContextUser(ctx, ctx.FormInt64("org"))
 | 
						ctxUser := checkContextUser(ctx, ctx.FormInt64("org"))
 | 
				
			||||||
	if ctx.Written() {
 | 
						if ctx.Written() {
 | 
				
			||||||
@@ -288,6 +289,7 @@ func CreatePost(ctx *context.Context) {
 | 
				
			|||||||
			AutoInit:      form.AutoInit,
 | 
								AutoInit:      form.AutoInit,
 | 
				
			||||||
			IsTemplate:    form.Template,
 | 
								IsTemplate:    form.Template,
 | 
				
			||||||
			TrustModel:    repo_model.ToTrustModel(form.TrustModel),
 | 
								TrustModel:    repo_model.ToTrustModel(form.TrustModel),
 | 
				
			||||||
 | 
								ObjectFormat:  form.ObjectFormat,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
 | 
								log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -388,20 +388,21 @@ func LFSFileFind(ctx *context.Context) {
 | 
				
			|||||||
	sha := ctx.FormString("sha")
 | 
						sha := ctx.FormString("sha")
 | 
				
			||||||
	ctx.Data["Title"] = oid
 | 
						ctx.Data["Title"] = oid
 | 
				
			||||||
	ctx.Data["PageIsSettingsLFS"] = true
 | 
						ctx.Data["PageIsSettingsLFS"] = true
 | 
				
			||||||
	var hash git.SHA1
 | 
						objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat()
 | 
				
			||||||
 | 
						var objectID git.ObjectID
 | 
				
			||||||
	if len(sha) == 0 {
 | 
						if len(sha) == 0 {
 | 
				
			||||||
		pointer := lfs.Pointer{Oid: oid, Size: size}
 | 
							pointer := lfs.Pointer{Oid: oid, Size: size}
 | 
				
			||||||
		hash = git.ComputeBlobHash([]byte(pointer.StringContent()))
 | 
							objectID = git.ComputeBlobHash(objectFormat, []byte(pointer.StringContent()))
 | 
				
			||||||
		sha = hash.String()
 | 
							sha = objectID.String()
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		hash = git.MustIDFromString(sha)
 | 
							objectID = objectFormat.MustIDFromString(sha)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
 | 
						ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
 | 
				
			||||||
	ctx.Data["Oid"] = oid
 | 
						ctx.Data["Oid"] = oid
 | 
				
			||||||
	ctx.Data["Size"] = size
 | 
						ctx.Data["Size"] = size
 | 
				
			||||||
	ctx.Data["SHA"] = sha
 | 
						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 {
 | 
						if err != nil && err != io.EOF {
 | 
				
			||||||
		log.Error("Failure in FindLFSFile: %v", err)
 | 
							log.Error("Failure in FindLFSFile: %v", err)
 | 
				
			||||||
		ctx.ServerError("LFSFind: FindLFSFile.", err)
 | 
							ctx.ServerError("LFSFind: FindLFSFile.", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -655,8 +655,14 @@ func TestWebhook(ctx *context.Context) {
 | 
				
			|||||||
	commit := ctx.Repo.Commit
 | 
						commit := ctx.Repo.Commit
 | 
				
			||||||
	if commit == nil {
 | 
						if commit == nil {
 | 
				
			||||||
		ghost := user_model.NewGhostUser()
 | 
							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{
 | 
							commit = &git.Commit{
 | 
				
			||||||
			ID:            git.MustIDFromString(git.EmptySHA),
 | 
								ID:            objectFormat.NewEmptyID(),
 | 
				
			||||||
			Author:        ghost.NewGitSig(),
 | 
								Author:        ghost.NewGitSig(),
 | 
				
			||||||
			Committer:     ghost.NewGitSig(),
 | 
								Committer:     ghost.NewGitSig(),
 | 
				
			||||||
			CommitMessage: "This is a fake commit",
 | 
								CommitMessage: "This is a fake commit",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	git_model "code.gitea.io/gitea/models/git"
 | 
						git_model "code.gitea.io/gitea/models/git"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						git "code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	webhook_module "code.gitea.io/gitea/modules/webhook"
 | 
						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()
 | 
						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{
 | 
						if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
 | 
				
			||||||
		Repo:    repo,
 | 
							Repo:    repo,
 | 
				
			||||||
		SHA:     sha,
 | 
							SHA:     commitID,
 | 
				
			||||||
		Creator: creator,
 | 
							Creator: creator,
 | 
				
			||||||
		CommitStatus: &git_model.CommitStatus{
 | 
							CommitStatus: &git_model.CommitStatus{
 | 
				
			||||||
			SHA:         sha,
 | 
								SHA:         sha,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,9 +36,10 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	topicBranch = opts.GitPushOptions["topic"]
 | 
						topicBranch = opts.GitPushOptions["topic"]
 | 
				
			||||||
	_, forcePush = opts.GitPushOptions["force-push"]
 | 
						_, forcePush = opts.GitPushOptions["force-push"]
 | 
				
			||||||
 | 
						objectFormat, _ := gitRepo.GetObjectFormat()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range opts.OldCommitIDs {
 | 
						for i := range opts.OldCommitIDs {
 | 
				
			||||||
		if opts.NewCommitIDs[i] == git.EmptySHA {
 | 
							if opts.NewCommitIDs[i] == objectFormat.Empty().String() {
 | 
				
			||||||
			results = append(results, private.HookProcReceiveRefResult{
 | 
								results = append(results, private.HookProcReceiveRefResult{
 | 
				
			||||||
				OriginalRef: opts.RefFullNames[i],
 | 
									OriginalRef: opts.RefFullNames[i],
 | 
				
			||||||
				OldOID:      opts.OldCommitIDs[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)
 | 
								log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								objectFormat, _ := gitRepo.GetObjectFormat()
 | 
				
			||||||
			results = append(results, private.HookProcReceiveRefResult{
 | 
								results = append(results, private.HookProcReceiveRefResult{
 | 
				
			||||||
				Ref:         pr.GetGitRefName(),
 | 
									Ref:         pr.GetGitRefName(),
 | 
				
			||||||
				OriginalRef: opts.RefFullNames[i],
 | 
									OriginalRef: opts.RefFullNames[i],
 | 
				
			||||||
				OldOID:      git.EmptySHA,
 | 
									OldOID:      objectFormat.Empty().String(),
 | 
				
			||||||
				NewOID:      opts.NewCommitIDs[i],
 | 
									NewOID:      opts.NewCommitIDs[i],
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,12 +19,12 @@ import (
 | 
				
			|||||||
func TestToCommitMeta(t *testing.T) {
 | 
					func TestToCommitMeta(t *testing.T) {
 | 
				
			||||||
	assert.NoError(t, unittest.PrepareTestDatabase())
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
	headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 | 
						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)}
 | 
						signature := &git.Signature{Name: "Test Signature", Email: "test@email.com", When: time.Unix(0, 0)}
 | 
				
			||||||
	tag := &git.Tag{
 | 
						tag := &git.Tag{
 | 
				
			||||||
		Name:    "Test Tag",
 | 
							Name:    "Test Tag",
 | 
				
			||||||
		ID:      sha1,
 | 
							ID:      sha1.Empty(),
 | 
				
			||||||
		Object:  sha1,
 | 
							Object:  sha1.Empty(),
 | 
				
			||||||
		Type:    "Test Type",
 | 
							Type:    "Test Type",
 | 
				
			||||||
		Tagger:  signature,
 | 
							Tagger:  signature,
 | 
				
			||||||
		Message: "Test Message",
 | 
							Message: "Test Message",
 | 
				
			||||||
@@ -34,8 +34,8 @@ func TestToCommitMeta(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	assert.NotNil(t, commitMeta)
 | 
						assert.NotNil(t, commitMeta)
 | 
				
			||||||
	assert.EqualValues(t, &api.CommitMeta{
 | 
						assert.EqualValues(t, &api.CommitMeta{
 | 
				
			||||||
		SHA:     "0000000000000000000000000000000000000000",
 | 
							SHA:     sha1.Empty().String(),
 | 
				
			||||||
		URL:     util.URLJoin(headRepo.APIURL(), "git/commits", "0000000000000000000000000000000000000000"),
 | 
							URL:     util.URLJoin(headRepo.APIURL(), "git/commits", sha1.Empty().String()),
 | 
				
			||||||
		Created: time.Unix(0, 0),
 | 
							Created: time.Unix(0, 0),
 | 
				
			||||||
	}, commitMeta)
 | 
						}, commitMeta)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import (
 | 
				
			|||||||
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						issues_model "code.gitea.io/gitea/models/issues"
 | 
				
			||||||
	project_model "code.gitea.io/gitea/models/project"
 | 
						project_model "code.gitea.io/gitea/models/project"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/structs"
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
@@ -53,6 +54,7 @@ type CreateRepoForm struct {
 | 
				
			|||||||
	TrustModel      string
 | 
						TrustModel      string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ForkSingleBranch string
 | 
						ForkSingleBranch string
 | 
				
			||||||
 | 
						ObjectFormat     git.ObjectFormat
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1115,10 +1115,15 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmdDiff := git.NewCommand(gitRepo.Ctx)
 | 
						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").
 | 
							cmdDiff.AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M").
 | 
				
			||||||
			AddArguments(opts.WhitespaceBehavior...).
 | 
								AddArguments(opts.WhitespaceBehavior...).
 | 
				
			||||||
			AddArguments("4b825dc642cb6eb9a060e54bf8d69288fbee4904"). // append empty tree ref
 | 
								AddDynamicArguments(objectFormat.EmptyTree().String()).
 | 
				
			||||||
			AddDynamicArguments(opts.AfterCommitID)
 | 
								AddDynamicArguments(opts.AfterCommitID)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		actualBeforeCommitID := opts.BeforeCommitID
 | 
							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}
 | 
						diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
 | 
				
			||||||
	if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA {
 | 
						if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == objectFormat.Empty().String() {
 | 
				
			||||||
		diffPaths = []string{git.EmptyTreeSHA, opts.AfterCommitID}
 | 
							diffPaths = []string{objectFormat.EmptyTree().String(), opts.AfterCommitID}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
 | 
						diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
 | 
				
			||||||
	if err != nil && strings.Contains(err.Error(), "no merge base") {
 | 
						if err != nil && strings.Contains(err.Error(), "no merge base") {
 | 
				
			||||||
@@ -1256,12 +1261,15 @@ func GetPullDiffStats(gitRepo *git.Repository, opts *DiffOptions) (*PullDiffStat
 | 
				
			|||||||
		separator = ".."
 | 
							separator = ".."
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID}
 | 
						objectFormat, err := gitRepo.GetObjectFormat()
 | 
				
			||||||
	if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA {
 | 
						if err != nil {
 | 
				
			||||||
		diffPaths = []string{git.EmptyTreeSHA, opts.AfterCommitID}
 | 
							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...)
 | 
						_, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...)
 | 
				
			||||||
	if err != nil && strings.Contains(err.Error(), "no merge base") {
 | 
						if err != nil && strings.Contains(err.Error(), "no merge base") {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,16 +48,18 @@ func CheckAndEnsureSafePR(pr *base.PullRequest, commonCloneBaseURL string, g bas
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SECURITY: SHAs Must be a SHA
 | 
						// 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)
 | 
							WarnAndNotice("PR #%d in %s has invalid MergeCommitSHA: %s", pr.Number, g, pr.MergeCommitSHA)
 | 
				
			||||||
		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)
 | 
							WarnAndNotice("PR #%d in %s has invalid HeadSHA: %s", pr.Number, g, pr.Head.SHA)
 | 
				
			||||||
		pr.Head.SHA = ""
 | 
							pr.Head.SHA = ""
 | 
				
			||||||
		valid = false
 | 
							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)
 | 
							WarnAndNotice("PR #%d in %s has invalid BaseSHA: %s", pr.Number, g, pr.Base.SHA)
 | 
				
			||||||
		pr.Base.SHA = ""
 | 
							pr.Base.SHA = ""
 | 
				
			||||||
		valid = false
 | 
							valid = false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -892,7 +892,8 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
 | 
				
			|||||||
				comment.UpdatedAt = comment.CreatedAt
 | 
									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)
 | 
									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
 | 
									comment.CommitID = headCommitID
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -232,7 +232,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
 | 
				
			|||||||
	//
 | 
						//
 | 
				
			||||||
	fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 | 
						fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 | 
				
			||||||
	baseRef := "master"
 | 
						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()})
 | 
						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, 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))
 | 
						assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", fromRepo.RepoPath())), 0o644))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
									log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err)
 | 
				
			||||||
				continue
 | 
									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{
 | 
								notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{
 | 
				
			||||||
				RefFullName: result.refName,
 | 
									RefFullName: result.refName,
 | 
				
			||||||
				OldCommitID: git.EmptySHA,
 | 
									OldCommitID: objectFormat.Empty().String(),
 | 
				
			||||||
				NewCommitID: commitID,
 | 
									NewCommitID: commitID,
 | 
				
			||||||
			}, repo_module.NewPushCommits())
 | 
								}, repo_module.NewPushCommits())
 | 
				
			||||||
			notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName, commitID)
 | 
								notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName, commitID)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -271,7 +271,7 @@ func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *re
 | 
				
			|||||||
		if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
 | 
							if !git.IsErrBranchNotExist(err) || !repo.IsEmpty {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err := t.Init(); err != nil {
 | 
							if err := t.Init(repo.ObjectFormat); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
							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
 | 
						// Get the commit from BaseBranch where the pull request got merged
 | 
				
			||||||
	mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse").
 | 
						mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse").
 | 
				
			||||||
		AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch).
 | 
							AddDynamicArguments(prHeadCommitID + ".." + pr.BaseBranch).
 | 
				
			||||||
		RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
 | 
							RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err)
 | 
							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
 | 
							// PR was maybe fast-forwarded, so just use last commit of PR
 | 
				
			||||||
		mergeCommit = prHeadCommitID
 | 
							mergeCommit = prHeadCommitID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mergeCommit = strings.TrimSpace(mergeCommit)
 | 
						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)
 | 
						commit, err := gitRepo.GetCommit(mergeCommit)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("GetMergeCommit[%s]: %w", mergeCommit, err)
 | 
							return nil, fmt.Errorf("GetMergeCommit[%s]: %w", mergeCommit, err)
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user