mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 08:21:11 +00:00 
			
		
		
		
	- `testPatch` is a function that is called to test a pull request and determine the state of the pull request. Checking for merge conflicts, check if the diff is empty and if the pull request modifies any protected files. - The checking for merge conflict and if the diff is empty used git commands that relied on a working tree to correctly functions. Forgejo store repositories in a bare format which do not contain a working tree. This means that a temporary copy was created every time a pull request had to be re-checked and for large repositories involving quite some I/O interaction. - This patch adjusts those codepaths to instead use newer Git plumbing commands that work without requiring a work tree and can thus be used directly on the bare repository. The merge conflict is now done via [`git-merge-tree(1)`](https://git-scm.com/docs/git-merge-tree/) and checking if the diff is empty is done via [`git-diff-tree(1)`](https://git-scm.com/docs/git-diff-tree). - If the function is called to test a patch where the head and base repository are not the same, then [Git alternate](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefalternateobjectdatabaseaalternateobjectdatabase) is used to make the head commit available in the base repository, this done on a per git command basis via the `GIT_ALTERNATE_OBJECT_DIRECTORIES` environment. - As far as I can understand the documentation and the existing code, there's no edge case that the new code cannot handle. It also results in a cleaner codepath, as the existing code did a lot of checking and merging in a more traditional approach that required a lot of (parsing) code, while the new code offloads this to git and has a trivial parser of the output. - Resolves forgejo/forgejo#7701 - Added exhaustive integration testing. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7727 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Reviewed-by: Otto <otto@codeberg.org> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
		
			
				
	
	
		
			113 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2025 The Forgejo Authors. All rights reserved.
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package repository
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"slices"
 | 
						|
 | 
						|
	git_model "forgejo.org/models/git"
 | 
						|
	repo_model "forgejo.org/models/repo"
 | 
						|
	user_model "forgejo.org/models/user"
 | 
						|
	"forgejo.org/modules/git"
 | 
						|
	repo_module "forgejo.org/modules/repository"
 | 
						|
	api "forgejo.org/modules/structs"
 | 
						|
)
 | 
						|
 | 
						|
// SyncFork syncs a branch of a fork with the base repo
 | 
						|
func SyncFork(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, branch string) error {
 | 
						|
	err := repo.MustNotBeArchived()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	err = repo.GetBaseRepo(ctx)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	err = git.Push(ctx, repo.BaseRepo.RepoPath(), git.PushOptions{
 | 
						|
		Remote: repo.RepoPath(),
 | 
						|
		Branch: fmt.Sprintf("%s:%s", branch, branch),
 | 
						|
		Env:    repo_module.PushingEnvironment(doer, repo),
 | 
						|
	})
 | 
						|
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// CanSyncFork returns information about syncing a fork
 | 
						|
func GetSyncForkInfo(ctx context.Context, repo *repo_model.Repository, branch string) (*api.SyncForkInfo, error) {
 | 
						|
	info := new(api.SyncForkInfo)
 | 
						|
 | 
						|
	if !repo.IsFork {
 | 
						|
		return info, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if repo.IsArchived {
 | 
						|
		return info, nil
 | 
						|
	}
 | 
						|
 | 
						|
	err := repo.GetBaseRepo(ctx)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	forkBranch, err := git_model.GetBranch(ctx, repo.ID, branch)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	info.ForkCommit = forkBranch.CommitID
 | 
						|
 | 
						|
	baseBranch, err := git_model.GetBranch(ctx, repo.BaseRepo.ID, branch)
 | 
						|
	if err != nil {
 | 
						|
		if git_model.IsErrBranchNotExist(err) {
 | 
						|
			// If the base repo don't have the branch, we don't need to continue
 | 
						|
			return info, nil
 | 
						|
		}
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	info.BaseCommit = baseBranch.CommitID
 | 
						|
 | 
						|
	// If both branches has the same latest commit, we don't need to sync
 | 
						|
	if forkBranch.CommitID == baseBranch.CommitID {
 | 
						|
		return info, nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Check if the latest commit of the fork is also in the base
 | 
						|
	gitRepo, err := git.OpenRepository(ctx, repo.BaseRepo.RepoPath())
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	defer gitRepo.Close()
 | 
						|
 | 
						|
	commit, err := gitRepo.GetCommit(forkBranch.CommitID)
 | 
						|
	if err != nil {
 | 
						|
		if git.IsErrNotExist(err) {
 | 
						|
			return info, nil
 | 
						|
		}
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	branchList, err := commit.GetAllBranches()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if !slices.Contains(branchList, branch) {
 | 
						|
		return info, nil
 | 
						|
	}
 | 
						|
 | 
						|
	diff, err := git.GetDivergingCommits(ctx, repo.BaseRepo.RepoPath(), baseBranch.CommitID, forkBranch.CommitID, nil)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	info.Allowed = true
 | 
						|
	info.CommitsBehind = diff.Behind
 | 
						|
 | 
						|
	return info, nil
 | 
						|
}
 |