mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
2978 files. The fork is 2026-07-15 (rebrand: gitea.dev -> github.com/hanzoai/git), so 2026 is the year, and "Copyright 2026 Hanzo AI, Inc." was already the form used by the handful of files that had ours. Shape is now: // Copyright 2026 Hanzo AI, Inc. All rights reserved. // Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT Ours on top, theirs retained verbatim. Retained because MIT requires it, and separately because they are true — those authors did write that code. Not renamed to "The Git Authors": Git is a different project under a different license, and putting its name here would attribute this code to people who have nothing to do with it. Adding our line is the fix; overwriting theirs would have been both a licence violation and a false statement. gofmt still reports import-order drift in some of these files. It predates this change (verified against a stash) and is left alone rather than burying a one-line-per-file legal change under 3000 files of unrelated reordering. Build clean. Co-authored-by: Hanzo Dev <[email protected]>
108 lines
3.7 KiB
Go
108 lines
3.7 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package pull
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
issues_model "github.com/hanzoai/git/models/issues"
|
|
repo_model "github.com/hanzoai/git/models/repo"
|
|
user_model "github.com/hanzoai/git/models/user"
|
|
"github.com/hanzoai/git/modules/git"
|
|
"github.com/hanzoai/git/modules/git/gitcmd"
|
|
"github.com/hanzoai/git/modules/log"
|
|
repo_module "github.com/hanzoai/git/modules/repository"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
)
|
|
|
|
// updateHeadByRebaseOnToBase handles updating a PR's head branch by rebasing it on the PR current base branch
|
|
func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) error {
|
|
// "Clone" base repo and add the cache headers for the head repo and branch
|
|
mergeCtx, cancel, err := createTemporaryRepoForMerge(ctx, pr, doer, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cancel()
|
|
|
|
// Determine the old merge-base before the rebase - we use this for LFS push later on
|
|
oldMergeBase, _, _ := gitcmd.NewCommand("merge-base").AddDashesAndList(tmpRepoBaseBranch, tmpRepoTrackingBranch).
|
|
WithDir(mergeCtx.tmpBasePath).RunStdString(ctx)
|
|
oldMergeBase = strings.TrimSpace(oldMergeBase)
|
|
|
|
// Rebase the tracking branch on to the base as the staging branch
|
|
if err := rebaseTrackingOnToBase(mergeCtx, repo_model.MergeStyleRebaseUpdate); err != nil {
|
|
return err
|
|
}
|
|
|
|
if setting.LFS.StartServer {
|
|
// Now we need to ensure that the head repository contains any LFS objects between the new base and the old mergebase
|
|
// It's questionable about where this should go - either after or before the push
|
|
// I think in the interests of data safety - failures to push to the lfs should prevent
|
|
// the push as you can always re-rebase.
|
|
if err := LFSPush(ctx, mergeCtx.tmpBasePath, tmpRepoBaseBranch, oldMergeBase, &issues_model.PullRequest{
|
|
HeadRepoID: pr.BaseRepoID,
|
|
BaseRepoID: pr.HeadRepoID,
|
|
}); err != nil {
|
|
log.Error("Unable to push lfs objects between %s and %s up to head branch in %-v: %v", tmpRepoBaseBranch, oldMergeBase, pr, err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Now determine who the pushing author should be
|
|
var headUser *user_model.User
|
|
if err := pr.HeadRepo.LoadOwner(ctx); err != nil {
|
|
if !user_model.IsErrUserNotExist(err) {
|
|
log.Error("Can't find user: %d for head repository in %-v - %v", pr.HeadRepo.OwnerID, pr, err)
|
|
return err
|
|
}
|
|
log.Error("Can't find user: %d for head repository in %-v - defaulting to doer: %-v - %v", pr.HeadRepo.OwnerID, pr, doer, err)
|
|
headUser = doer
|
|
} else {
|
|
headUser = pr.HeadRepo.Owner
|
|
}
|
|
|
|
pushCmd := gitcmd.NewCommand("push", "-f", "head_repo").
|
|
AddDynamicArguments(tmpRepoStagingBranch + ":" + git.BranchPrefix + pr.HeadBranch)
|
|
|
|
// Push back to the head repository.
|
|
// TODO: this cause an api call to "/v1/internal/hook/post-receive/...",
|
|
// that prevents us from doint the whole merge in one db transaction
|
|
mergeCtx.outbuf.Reset()
|
|
|
|
if err := pushCmd.
|
|
WithEnv(repo_module.FullPushingEnvironment(
|
|
headUser,
|
|
doer,
|
|
pr.HeadRepo,
|
|
pr.HeadRepo.Name,
|
|
pr.ID,
|
|
pr.Index,
|
|
)).
|
|
WithDir(mergeCtx.tmpBasePath).
|
|
WithStdoutBuffer(mergeCtx.outbuf).
|
|
RunWithStderr(ctx); err != nil {
|
|
if strings.Contains(err.Stderr(), "non-fast-forward") {
|
|
return &git.ErrPushOutOfDate{
|
|
StdOut: mergeCtx.outbuf.String(),
|
|
StdErr: err.Stderr(),
|
|
Err: err,
|
|
}
|
|
} else if strings.Contains(err.Stderr(), "! [remote rejected]") {
|
|
err := &git.ErrPushRejected{
|
|
StdOut: mergeCtx.outbuf.String(),
|
|
StdErr: err.Stderr(),
|
|
Err: err,
|
|
}
|
|
err.GenerateMessage()
|
|
return err
|
|
}
|
|
return fmt.Errorf("git push: %s", err.Stderr())
|
|
}
|
|
mergeCtx.outbuf.Reset()
|
|
return nil
|
|
}
|