mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-22 06:15:18 +02:00
before: gitrepo vs git packages after: git package fully handle all git operations by the way, use `WithRepo(repo)` instead of `WithDir(repo.Path)` to hide path details. benefits: 1. remove all unnecessary wrappers, developers no need to struggle with "which package should be used" 2. simplify code, RepositoryFacade can (will) be used everywhere, all "path" details are (will be) hidden
113 lines
4.2 KiB
Go
113 lines
4.2 KiB
Go
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package git
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
repo_model "gitea.dev/models/repo"
|
|
"gitea.dev/modules/git"
|
|
"gitea.dev/modules/util"
|
|
)
|
|
|
|
// CompareInfo represents needed information for comparing references.
|
|
type CompareInfo struct {
|
|
BaseRepo *repo_model.Repository
|
|
BaseRef git.RefName
|
|
BaseCommitID string
|
|
HeadRepo *repo_model.Repository
|
|
HeadGitRepo *git.Repository
|
|
HeadRef git.RefName
|
|
HeadCommitID string
|
|
|
|
CompareSeparator string
|
|
|
|
// CompareBase is the left-side commit ID used for comparing
|
|
// for "...": it is merge base (empty for no merge base)
|
|
// for direct comparison "..": it is base commit ID
|
|
CompareBase string
|
|
|
|
Commits []*git.Commit
|
|
NumFiles int
|
|
}
|
|
|
|
func (ci *CompareInfo) IsSameRepository() bool {
|
|
return ci.BaseRepo.ID == ci.HeadRepo.ID
|
|
}
|
|
|
|
func (ci *CompareInfo) IsSameRef() bool {
|
|
return ci.IsSameRepository() && ci.BaseRef == ci.HeadRef
|
|
}
|
|
|
|
func (ci *CompareInfo) DirectComparison() bool {
|
|
// FIXME: the design of "DirectComparison" is wrong, it loses the information of `^`
|
|
// To correctly handle the comparison, developers should use `ci.CompareSeparator` directly, all "DirectComparison" related code should be rewritten.
|
|
return ci.CompareSeparator == ".."
|
|
}
|
|
|
|
// GetCompareInfo generates and returns compare information between base and head branches of repositories.
|
|
// It does its best to fill the fields as many as it can.
|
|
// MergeBase can be empty if the base and head are unrelated.
|
|
func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Repository, headGitRepo *git.Repository, baseRef, headRef git.RefName, directComparison, fileOnly bool) (compareInfo CompareInfo, err error) {
|
|
baseCommitID, err1 := git.GetFullCommitID(ctx, baseRepo, baseRef.String())
|
|
headCommitID, err2 := git.GetFullCommitID(ctx, headRepo, headRef.String())
|
|
compareInfo = CompareInfo{
|
|
BaseRepo: baseRepo,
|
|
BaseRef: baseRef,
|
|
BaseCommitID: baseCommitID,
|
|
HeadRepo: headRepo,
|
|
HeadGitRepo: headGitRepo,
|
|
HeadRef: headRef,
|
|
HeadCommitID: headCommitID,
|
|
CompareSeparator: util.Iif(directComparison, "..", "..."),
|
|
}
|
|
if err1 != nil || err2 != nil {
|
|
return compareInfo, errors.Join(err1, err2)
|
|
}
|
|
|
|
// if they are not the same repository, then we need to fetch the base commit into the head repository
|
|
// because we will use headGitRepo in the following code
|
|
if baseRepo.ID != headRepo.ID {
|
|
exist := headGitRepo.IsReferenceExist(ctx, compareInfo.BaseCommitID)
|
|
if !exist {
|
|
if err := git.FetchRemoteCommit(ctx, headRepo, baseRepo, compareInfo.BaseCommitID); err != nil {
|
|
return compareInfo, fmt.Errorf("FetchRemoteCommit: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !directComparison {
|
|
compareInfo.CompareBase, err = git.MergeBase(ctx, headRepo, compareInfo.BaseCommitID, compareInfo.HeadCommitID)
|
|
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
|
return compareInfo, fmt.Errorf("MergeBase: %w", err)
|
|
}
|
|
} else {
|
|
compareInfo.CompareBase = compareInfo.BaseCommitID
|
|
}
|
|
|
|
if compareInfo.CompareBase == "" {
|
|
return compareInfo, nil
|
|
}
|
|
|
|
// We have a common base - therefore we know that ... should work
|
|
if !fileOnly {
|
|
// In git log/rev-list, the "..." syntax represents the symmetric difference between two references,
|
|
// which is different from the meaning of "..." in git diff (where it implies diffing from the merge base).
|
|
// For listing PR commits, we must use merge-base..head to include only the commits introduced by the head branch.
|
|
// Otherwise, commits newly pushed to the base branch would also be included, which is incorrect.
|
|
compareInfo.Commits, err = headGitRepo.ShowPrettyFormatLogToList(ctx, compareInfo.CompareBase+".."+compareInfo.HeadCommitID)
|
|
if err != nil {
|
|
return compareInfo, fmt.Errorf("ShowPrettyFormatLogToList: %w", err)
|
|
}
|
|
}
|
|
|
|
// Count number of changed files.
|
|
// TODO: This probably should be removed as we need to use shortstat elsewhere
|
|
// Now there is git diff --shortstat but this appears to be slower than simply iterating with --nameonly
|
|
compareInfo.NumFiles, err = headGitRepo.GetDiffNumChangedFiles(ctx, compareInfo.BaseCommitID, compareInfo.HeadCommitID, directComparison)
|
|
return compareInfo, err
|
|
}
|