mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-13 19:19:52 +00:00
fix: git push hook post receive (#38089)
* fix incorrect delayWriter call (there is already a defer call) * split HookPostReceive into small functions * fix incorrect HookPostReceiveResult response for errors * fix incorrect AddRepoToLicenseUpdaterQueue call * make sure repo home and branches page can work without default branch * make sure default branch is always synchronized between database and git repo, and fix FIXME
This commit is contained in:
+42
-97
@@ -151,9 +151,6 @@ func (d *delayWriter) WriteString(s string) (n int, err error) {
|
||||
}
|
||||
|
||||
func (d *delayWriter) Close() error {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
stopped := d.timer.Stop()
|
||||
if stopped || d.buf == nil {
|
||||
return nil
|
||||
@@ -163,16 +160,6 @@ func (d *delayWriter) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
type nilWriter struct{}
|
||||
|
||||
func (n *nilWriter) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (n *nilWriter) WriteString(s string) (int, error) {
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
func parseGitHookCommitRefLine(line string) (oldCommitID, newCommitID string, refFullName git.RefName, ok bool) {
|
||||
fields := strings.Split(line, " ")
|
||||
if len(fields) != 3 {
|
||||
@@ -227,8 +214,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
total := 0
|
||||
lastline := 0
|
||||
|
||||
var out io.Writer
|
||||
out = &nilWriter{}
|
||||
out := io.Discard
|
||||
if setting.Git.VerbosePush {
|
||||
if setting.Git.VerbosePushDelay > 0 {
|
||||
dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
|
||||
@@ -350,12 +336,10 @@ Gitea or set your environment appropriately.`, "")
|
||||
return nil
|
||||
}
|
||||
|
||||
var out io.Writer
|
||||
var dWriter *delayWriter
|
||||
out = &nilWriter{}
|
||||
out := io.Discard
|
||||
if setting.Git.VerbosePush {
|
||||
if setting.Git.VerbosePushDelay > 0 {
|
||||
dWriter = newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
|
||||
dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
|
||||
defer dWriter.Close()
|
||||
out = dWriter
|
||||
} else {
|
||||
@@ -382,101 +366,62 @@ Gitea or set your environment appropriately.`, "")
|
||||
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
|
||||
IsWiki: isWiki,
|
||||
}
|
||||
oldCommitIDs := make([]string, hookBatchSize)
|
||||
newCommitIDs := make([]string, hookBatchSize)
|
||||
refFullNames := make([]git.RefName, hookBatchSize)
|
||||
count := 0
|
||||
total := 0
|
||||
wasEmpty := false
|
||||
masterPushed := false
|
||||
|
||||
oldCommitIDs := make([]string, 0, hookBatchSize)
|
||||
newCommitIDs := make([]string, 0, hookBatchSize)
|
||||
refFullNames := make([]git.RefName, 0, hookBatchSize)
|
||||
results := make([]private.HookPostReceiveBranchResult, 0)
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
// TODO: support news feeds for wiki
|
||||
if isWiki {
|
||||
continue
|
||||
}
|
||||
defer func() {
|
||||
hookPrintResults(results)
|
||||
}()
|
||||
|
||||
var ok bool
|
||||
oldCommitIDs[count], newCommitIDs[count], refFullNames[count], ok = parseGitHookCommitRefLine(scanner.Text())
|
||||
if !ok {
|
||||
continue
|
||||
processBatch := func() error {
|
||||
if len(refFullNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, ".")
|
||||
commitID, _ := git.NewIDFromString(newCommitIDs[count])
|
||||
if refFullNames[count] == git.BranchPrefix+"master" && !commitID.IsZero() && count == total {
|
||||
masterPushed = true
|
||||
}
|
||||
count++
|
||||
total++
|
||||
|
||||
if count >= hookBatchSize {
|
||||
fmt.Fprintf(out, " Processing %d references\n", count)
|
||||
_, _ = fmt.Fprintf(out, " Processing %d references\n", len(refFullNames))
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
resp, extra := private.HookPostReceive(ctx, repoUser, repoName, hookOptions)
|
||||
if extra.HasError() {
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
return fail(ctx, extra.UserMsg, "HookPostReceive failed: %v", extra.Error)
|
||||
}
|
||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||
results = append(results, resp.Results...)
|
||||
count = 0
|
||||
oldCommitIDs = oldCommitIDs[:0]
|
||||
newCommitIDs = newCommitIDs[:0]
|
||||
refFullNames = refFullNames[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
// wiki doesn't need "post-receive" at the moment
|
||||
if isWiki {
|
||||
continue
|
||||
}
|
||||
|
||||
oldCommitID, newCommitID, refFullName, ok := parseGitHookCommitRefLine(scanner.Text())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintf(out, ".")
|
||||
|
||||
oldCommitIDs = append(oldCommitIDs, oldCommitID)
|
||||
newCommitIDs = append(newCommitIDs, newCommitID)
|
||||
refFullNames = append(refFullNames, refFullName)
|
||||
if len(refFullNames) >= hookBatchSize {
|
||||
// process and start a new batch
|
||||
if err := processBatch(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
return fail(ctx, "Hook failed: stdin read error", "scanner error: %v", err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
if wasEmpty && masterPushed {
|
||||
// We need to tell the repo to reset the default branch to master
|
||||
extra := private.SetDefaultBranch(ctx, repoUser, repoName, "master")
|
||||
if extra.HasError() {
|
||||
return fail(ctx, extra.UserMsg, "SetDefaultBranch failed: %v", extra.Error)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, "Processed %d references in total\n", total)
|
||||
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
return nil
|
||||
}
|
||||
|
||||
hookOptions.OldCommitIDs = oldCommitIDs[:count]
|
||||
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
||||
hookOptions.RefFullNames = refFullNames[:count]
|
||||
|
||||
fmt.Fprintf(out, " Processing %d references\n", count)
|
||||
|
||||
resp, extra := private.HookPostReceive(ctx, repoUser, repoName, hookOptions)
|
||||
if resp == nil {
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
return fail(ctx, extra.UserMsg, "HookPostReceive failed: %v", extra.Error)
|
||||
}
|
||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||
results = append(results, resp.Results...)
|
||||
|
||||
fmt.Fprintf(out, "Processed %d references in total\n", total)
|
||||
|
||||
if wasEmpty && masterPushed {
|
||||
// We need to tell the repo to reset the default branch to master
|
||||
extra := private.SetDefaultBranch(ctx, repoUser, repoName, "master")
|
||||
if extra.HasError() {
|
||||
return fail(ctx, extra.UserMsg, "SetDefaultBranch failed: %v", extra.Error)
|
||||
}
|
||||
}
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
|
||||
return nil
|
||||
return processBatch()
|
||||
}
|
||||
|
||||
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
||||
|
||||
@@ -36,25 +36,17 @@ func (repo *Repository) GetCommit(ref string) (*Commit, error) {
|
||||
|
||||
// GetBranchCommit returns the last commit of given branch.
|
||||
func (repo *Repository) GetBranchCommit(name string) (*Commit, error) {
|
||||
commitID, err := repo.GetBranchCommitID(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.GetCommit(commitID)
|
||||
return repo.GetCommit(RefNameFromBranch(name).String())
|
||||
}
|
||||
|
||||
// GetTagCommit get the commit of the specific tag via name
|
||||
func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
|
||||
commitID, err := repo.GetTagCommitID(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.GetCommit(commitID)
|
||||
return repo.GetCommit(RefNameFromTag(name).String())
|
||||
}
|
||||
|
||||
func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Commit, error) {
|
||||
// File name starts with ':' must be escaped.
|
||||
if relpath[0] == ':' {
|
||||
if strings.HasPrefix(relpath, ":") {
|
||||
relpath = `\` + relpath
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,6 @@ type SSHLogOption struct {
|
||||
// HookPostReceiveResult represents an individual result from PostReceive
|
||||
type HookPostReceiveResult struct {
|
||||
Results []HookPostReceiveBranchResult
|
||||
RepoWasEmpty bool
|
||||
Err string
|
||||
}
|
||||
|
||||
// HookPostReceiveBranchResult represents an individual branch result from PostReceive
|
||||
|
||||
@@ -13,6 +13,9 @@ type PushUpdateOptions struct {
|
||||
PusherName string
|
||||
RepoUserName string
|
||||
RepoName string
|
||||
|
||||
// FIXME: this struct's design is not right, the changed commits should be in a separate slice
|
||||
|
||||
RefFullName git.RefName // branch, tag or other name to push
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
|
||||
@@ -5,6 +5,7 @@ package private
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -29,29 +30,8 @@ import (
|
||||
repo_service "gitea.dev/services/repository"
|
||||
)
|
||||
|
||||
// HookPostReceive updates services and users
|
||||
func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
opts := web.GetForm(ctx).(*private.HookOptions)
|
||||
|
||||
// We don't rely on RepoAssignment here because:
|
||||
// a) we don't need the git repo in this function
|
||||
// OUT OF DATE: we do need the git repo to sync the branch to the db now.
|
||||
// b) our update function will likely change the repository in the db so we will need to refresh it
|
||||
// c) we don't always need the repo
|
||||
|
||||
ownerName := ctx.PathParam("owner")
|
||||
repoName := ctx.PathParam("repo")
|
||||
|
||||
// defer getting the repository at this point - as we should only retrieve it if we're going to call update
|
||||
var (
|
||||
repo *repo_model.Repository
|
||||
gitRepo *git.Repository
|
||||
)
|
||||
defer func() { _ = gitRepo.Close() }() // it's safe to call Close on a nil pointer, but it needs to use the latest value
|
||||
|
||||
func hookPostReceiveCollectPushUpdates(opts *private.HookOptions, repo *repo_model.Repository) []*repo_module.PushUpdateOptions {
|
||||
updates := make([]*repo_module.PushUpdateOptions, 0, len(opts.OldCommitIDs))
|
||||
wasEmpty := false
|
||||
|
||||
for i := range opts.OldCommitIDs {
|
||||
refFullName := opts.RefFullNames[i]
|
||||
|
||||
@@ -60,151 +40,124 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
// or other less-standard refs spaces are ignored since there
|
||||
// may be a very large number of them).
|
||||
if refFullName.IsBranch() || refFullName.IsTag() {
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
if ctx.Written() {
|
||||
// Error handled in loadRepository
|
||||
return
|
||||
}
|
||||
wasEmpty = repo.IsEmpty
|
||||
}
|
||||
|
||||
option := &repo_module.PushUpdateOptions{
|
||||
RefFullName: refFullName,
|
||||
OldCommitID: opts.OldCommitIDs[i],
|
||||
NewCommitID: opts.NewCommitIDs[i],
|
||||
PusherID: opts.UserID,
|
||||
PusherName: opts.UserName,
|
||||
RepoUserName: ownerName,
|
||||
RepoName: repoName,
|
||||
RepoUserName: repo.OwnerName,
|
||||
RepoName: repo.Name,
|
||||
}
|
||||
updates = append(updates, option)
|
||||
if repo.IsEmpty && (refFullName.BranchName() == "master" || refFullName.BranchName() == "main") {
|
||||
// put the master/main branch first
|
||||
// FIXME: It doesn't always work, since the master/main branch may not be the first batch of updates.
|
||||
// If the user pushes many branches at once, the Git hook will call the internal API in batches, rather than all at once.
|
||||
// See https://github.com/go-gitea/gitea/blob/cb52b17f92e2d2293f7c003649743464492bca48/cmd/hook.go#L27
|
||||
// If the user executes `git push origin --all` and pushes more than 30 branches, the master/main may not be the default branch.
|
||||
copy(updates[1:], updates)
|
||||
updates[0] = option
|
||||
}
|
||||
}
|
||||
return updates
|
||||
}
|
||||
|
||||
if repo != nil && len(updates) > 0 {
|
||||
func hookPostReceiveSyncDatabaseBranches(ctx *gitea_context.PrivateContext, opts *private.HookOptions, repo *repo_model.Repository, updates []*repo_module.PushUpdateOptions) bool {
|
||||
branchesToSync := make([]*repo_module.PushUpdateOptions, 0, len(updates))
|
||||
for _, update := range updates {
|
||||
if !update.RefFullName.IsBranch() {
|
||||
continue
|
||||
}
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
wasEmpty = repo.IsEmpty
|
||||
}
|
||||
|
||||
if update.IsDelRef() {
|
||||
if err := git_model.MarkBranchAsDeleted(ctx, repo.ID, update.RefFullName.BranchName(), update.PusherID); err != nil {
|
||||
log.Error("Failed to mark branch as deleted: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to mark branch as deleted: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, fmt.Sprintf("failed to mark branch %s as deleted", update.RefFullName))
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
branchesToSync = append(branchesToSync, update)
|
||||
|
||||
// TODO: should we return the error and return the error when pushing? Currently it will log the error and not prevent the pushing
|
||||
pull_service.UpdatePullsRefs(ctx, repo, update)
|
||||
}
|
||||
}
|
||||
if len(branchesToSync) > 0 {
|
||||
var err error
|
||||
gitRepo, err = gitrepo.OpenRepository(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
|
||||
if len(branchesToSync) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
branchNames = make([]string, 0, len(branchesToSync))
|
||||
commitIDs = make([]string, 0, len(branchesToSync))
|
||||
)
|
||||
gitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
|
||||
if err != nil {
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to open repository")
|
||||
return false
|
||||
}
|
||||
|
||||
branchNames := make([]string, 0, len(branchesToSync))
|
||||
commitIDs := make([]string, 0, len(branchesToSync))
|
||||
for _, update := range branchesToSync {
|
||||
branchNames = append(branchNames, update.RefFullName.BranchName())
|
||||
commitIDs = append(commitIDs, update.NewCommitID)
|
||||
}
|
||||
|
||||
if err := repo_service.SyncBranchesToDB(ctx, repo.ID, opts.UserID, branchNames, commitIDs, gitRepo.GetCommit); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to sync branch to DB in repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
if err = repo_service.SyncBranchesToDB(ctx, repo.ID, opts.UserID, branchNames, commitIDs, gitRepo.GetCommit); err != nil {
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to sync branch to DB")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// HookPostReceive updates services and users
|
||||
func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
opts := web.GetForm(ctx).(*private.HookOptions)
|
||||
if opts.IsWiki {
|
||||
setting.PanicInDevOrTesting("wiki hook-post-receive is not supported")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := repo_service.PushUpdates(updates); err != nil {
|
||||
log.Error("Failed to Update: %s/%s Total Updates: %d", ownerName, repoName, len(updates))
|
||||
for i, update := range updates {
|
||||
log.Error("Failed to Update: %s/%s Update: %d/%d: Branch: %s", ownerName, repoName, i, len(updates), update.RefFullName.BranchName())
|
||||
}
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// handle pull request merging, a pull request action should push at least 1 commit
|
||||
if opts.PushTrigger == repo_module.PushTriggerPRMergeToBase {
|
||||
handlePullRequestMerging(ctx, opts, ownerName, repoName, updates)
|
||||
ownerName := ctx.PathParam("owner")
|
||||
repoName := ctx.PathParam("repo")
|
||||
repo := loadRepository(ctx, ownerName, repoName)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
// now, repo can't be nil
|
||||
|
||||
// first, collect updates and sync branches
|
||||
updates := hookPostReceiveCollectPushUpdates(opts, repo)
|
||||
if !hookPostReceiveSyncDatabaseBranches(ctx, opts, repo, updates) {
|
||||
return
|
||||
}
|
||||
hookPostReceiveSyncRepoDefaultBranch(ctx, opts, repo)
|
||||
|
||||
// handle pull request merging, a pull request action should push at least 1 commit
|
||||
if opts.PushTrigger == repo_module.PushTriggerPRMergeToBase {
|
||||
if !hookPostReceiveHandlePullRequestMerging(ctx, opts, updates) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !hookPostReceiveUpdateRepoByOptions(ctx, opts, repo) {
|
||||
return
|
||||
}
|
||||
|
||||
// push async updates
|
||||
if err := repo_service.PushUpdates(updates...); err != nil {
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to push updates")
|
||||
return
|
||||
}
|
||||
|
||||
hookPostReceiveRespondWithTrailer(ctx, opts, repo)
|
||||
}
|
||||
|
||||
func hookPostReceiveUpdateRepoByOptions(ctx *gitea_context.PrivateContext, opts *private.HookOptions, repo *repo_model.Repository) bool {
|
||||
isPrivate := opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate)
|
||||
isTemplate := opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate)
|
||||
// Handle Push Options
|
||||
if isPrivate.Has() || isTemplate.Has() {
|
||||
// load the repository
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
if ctx.Written() {
|
||||
// Error handled in loadRepository
|
||||
return
|
||||
}
|
||||
wasEmpty = repo.IsEmpty
|
||||
}
|
||||
|
||||
pusher, err := loadContextCacheUser(ctx, opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to load pusher user")
|
||||
return false
|
||||
}
|
||||
perm, err := access_model.GetDoerRepoPermission(ctx, repo, pusher)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to load doer repo permission")
|
||||
return false
|
||||
}
|
||||
if !perm.IsOwner() && !perm.IsAdmin() {
|
||||
ctx.JSON(http.StatusNotFound, private.HookPostReceiveResult{
|
||||
Err: "Permissions denied",
|
||||
})
|
||||
return
|
||||
ctx.PrivateError(http.StatusNotFound, nil, "permission denied")
|
||||
return false
|
||||
}
|
||||
|
||||
// FIXME: these options are not quite right, for example: changing visibility should do more works than just setting the is_private flag
|
||||
@@ -213,46 +166,25 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
// TODO: it needs to do more work
|
||||
repo.IsPrivate = isPrivate.Value()
|
||||
if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_private"); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to change visibility"})
|
||||
log.Error("failed to update repo is_private: %v", err)
|
||||
}
|
||||
}
|
||||
if isTemplate.Has() && repo.IsTemplate != isTemplate.Value() {
|
||||
repo.IsTemplate = isTemplate.Value()
|
||||
if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_template"); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to change template status"})
|
||||
log.Error("failed to update repo is_template: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func hookPostReceiveRespondWithTrailer(ctx *gitea_context.PrivateContext, opts *private.HookOptions, repo *repo_model.Repository) {
|
||||
results := make([]private.HookPostReceiveBranchResult, 0, len(opts.OldCommitIDs))
|
||||
|
||||
// We have to reload the repo in case its state is changed above
|
||||
repo = nil
|
||||
var baseRepo *repo_model.Repository
|
||||
|
||||
// Now handle the pull request notification trailers
|
||||
for i := range opts.OldCommitIDs {
|
||||
refFullName := opts.RefFullNames[i]
|
||||
newCommitID := opts.NewCommitIDs[i]
|
||||
|
||||
// If we've pushed a branch (and not deleted it)
|
||||
if !git.IsEmptyCommitID(newCommitID) && refFullName.IsBranch() {
|
||||
// First ensure we have the repository loaded, we're allowed pulls requests and we can get the base repo
|
||||
if repo == nil {
|
||||
repo = loadRepository(ctx, ownerName, repoName)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
baseRepo = repo
|
||||
|
||||
baseRepo := repo
|
||||
if repo.IsFork {
|
||||
if err := repo.GetBaseRepo(ctx); err != nil {
|
||||
log.Error("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err),
|
||||
RepoWasEmpty: wasEmpty,
|
||||
})
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to load base repo")
|
||||
return
|
||||
}
|
||||
if repo.BaseRepo.AllowsPulls(ctx) {
|
||||
@@ -262,41 +194,30 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
|
||||
if !baseRepo.AllowsPulls(ctx) {
|
||||
// We can stop there's no need to go any further
|
||||
ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
|
||||
RepoWasEmpty: wasEmpty,
|
||||
})
|
||||
ctx.JSON(http.StatusOK, private.HookPostReceiveResult{})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Now handle the pull request notification trailers
|
||||
for i := range opts.OldCommitIDs {
|
||||
refFullName := opts.RefFullNames[i]
|
||||
newCommitID := opts.NewCommitIDs[i]
|
||||
|
||||
// If we've pushed a branch (and not deleted it)
|
||||
if !git.IsEmptyCommitID(newCommitID) && refFullName.IsBranch() {
|
||||
branch := refFullName.BranchName()
|
||||
|
||||
if branch == baseRepo.DefaultBranch {
|
||||
if err := repo_service.AddRepoToLicenseUpdaterQueue(&repo_service.LicenseUpdaterOptions{
|
||||
RepoID: repo.ID,
|
||||
}); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{Err: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if branch == baseRepo.DefaultBranch && !repo.IsFork {
|
||||
// If our branch is the default branch of an unforked repo - there's no PR to create or refer to
|
||||
if !repo.IsFork {
|
||||
results = append(results, private.HookPostReceiveBranchResult{})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetUnmergedPullRequest(ctx, repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch, issues_model.PullRequestFlowGithub)
|
||||
if err != nil && !issues_model.IsErrPullRequestNotExist(err) {
|
||||
log.Error("Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf(
|
||||
"Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err),
|
||||
RepoWasEmpty: wasEmpty,
|
||||
})
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to get active PR for branch "+branch)
|
||||
return
|
||||
}
|
||||
|
||||
if pr == nil {
|
||||
results = append(results, private.HookPostReceiveBranchResult{
|
||||
Message: setting.Git.PullRequestPushMessage && baseRepo.AllowsPulls(ctx),
|
||||
@@ -314,43 +235,79 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.JSON(http.StatusOK, private.HookPostReceiveResult{
|
||||
Results: results,
|
||||
RepoWasEmpty: wasEmpty,
|
||||
})
|
||||
ctx.JSON(http.StatusOK, private.HookPostReceiveResult{Results: results})
|
||||
}
|
||||
|
||||
func loadContextCacheUser(ctx context.Context, id int64) (*user_model.User, error) {
|
||||
return cache.GetWithContextCache(ctx, cachegroup.User, id, user_model.GetUserByID)
|
||||
}
|
||||
|
||||
// handlePullRequestMerging handle pull request merging, a pull request action should push at least 1 commit
|
||||
func handlePullRequestMerging(ctx *gitea_context.PrivateContext, opts *private.HookOptions, ownerName, repoName string, updates []*repo_module.PushUpdateOptions) {
|
||||
// hookPostReceiveHandlePullRequestMerging handle pull request merging, a pull request action should push at least 1 commit
|
||||
func hookPostReceiveHandlePullRequestMerging(ctx *gitea_context.PrivateContext, opts *private.HookOptions, updates []*repo_module.PushUpdateOptions) bool {
|
||||
if len(updates) == 0 {
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{
|
||||
Err: fmt.Sprintf("Pushing a merged PR (pr:%d) no commits pushed ", opts.PullRequestID),
|
||||
})
|
||||
return
|
||||
err := fmt.Errorf("Pushing a merged PR (pr:%d) no commits pushed ", opts.PullRequestID)
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "no push update")
|
||||
return false
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetPullRequestByID(ctx, opts.PullRequestID)
|
||||
if err != nil {
|
||||
log.Error("GetPullRequestByID[%d]: %v", opts.PullRequestID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "GetPullRequestByID failed"})
|
||||
return
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to load pull request")
|
||||
return false
|
||||
}
|
||||
|
||||
pusher, err := loadContextCacheUser(ctx, opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Load pusher user failed"})
|
||||
return
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to load pusher user")
|
||||
return false
|
||||
}
|
||||
|
||||
// FIXME: Maybe we need a `PullRequestStatusMerged` status for PRs that are merged, currently we use the previous status
|
||||
// here to keep it as before, that maybe PullRequestStatusMergeable
|
||||
if _, err := pull_service.SetMerged(ctx, pr, updates[len(updates)-1].NewCommitID, timeutil.TimeStampNow(), pusher, pr.Status); err != nil {
|
||||
log.Error("Failed to update PR to merged: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{Err: "Failed to update PR to merged"})
|
||||
_, err = pull_service.SetMerged(ctx, pr, updates[len(updates)-1].NewCommitID, timeutil.TimeStampNow(), pusher, pr.Status)
|
||||
if err != nil {
|
||||
ctx.PrivateError(http.StatusInternalServerError, err, "failed to set pr to merged")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func hookPostReceiveSyncRepoDefaultBranch(ctx *gitea_context.PrivateContext, opts *private.HookOptions, repo *repo_model.Repository) {
|
||||
hasBranch := false
|
||||
for _, refFullName := range opts.RefFullNames {
|
||||
if hasBranch = refFullName.IsBranch(); hasBranch {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasBranch {
|
||||
return
|
||||
}
|
||||
gitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("failed to open git repo: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// if default branch doesn't exist, try to guess one from existing git repo
|
||||
_, err = gitRepo.GetBranchCommitID(repo.DefaultBranch)
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
for _, guessBranchName := range []string{"main", "master"} {
|
||||
if _, err = gitRepo.GetBranchCommitID(guessBranchName); err == nil {
|
||||
repo.DefaultBranch = guessBranchName
|
||||
err = repo_model.UpdateDefaultBranch(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("failed to update default branch: %v", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if default branch was pushed, always keep the HEAD ref in sync
|
||||
for _, refFullName := range opts.RefFullNames {
|
||||
if refFullName.IsBranch() && refFullName.BranchName() == repo.DefaultBranch {
|
||||
_ = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ func TestHandlePullRequestMerging(t *testing.T) {
|
||||
autoMerge := unittest.AssertExistsAndLoadBean(t, &pull_model.AutoMerge{PullID: pr.ID})
|
||||
|
||||
ctx, resp := contexttest.MockPrivateContext(t, "/")
|
||||
handlePullRequestMerging(ctx, &private.HookOptions{
|
||||
hookPostReceiveHandlePullRequestMerging(ctx, &private.HookOptions{
|
||||
PullRequestID: pr.ID,
|
||||
UserID: 2,
|
||||
}, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, []*repo_module.PushUpdateOptions{
|
||||
}, []*repo_module.PushUpdateOptions{
|
||||
{NewCommitID: "01234567"},
|
||||
})
|
||||
assert.Empty(t, resp.Body.String())
|
||||
|
||||
@@ -52,13 +52,16 @@ func Branches(ctx *context.Context) {
|
||||
|
||||
kw := ctx.FormString("q")
|
||||
|
||||
defaultBranch, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, optional.None[bool](), kw, page, pageSize)
|
||||
defaultBranchOptional, branches, branchesCount, err := repo_service.LoadBranches(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, optional.None[bool](), kw, page, pageSize)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadBranches", err)
|
||||
return
|
||||
}
|
||||
|
||||
commitIDs := []string{defaultBranch.DBBranch.CommitID}
|
||||
commitIDs := make([]string, 0, len(branches)+1)
|
||||
if defaultBranchOptional != nil {
|
||||
commitIDs = append(commitIDs, defaultBranchOptional.DBBranch.CommitID)
|
||||
}
|
||||
for _, branch := range branches {
|
||||
commitIDs = append(commitIDs, branch.DBBranch.CommitID)
|
||||
}
|
||||
@@ -83,7 +86,7 @@ func Branches(ctx *context.Context) {
|
||||
ctx.Data["Branches"] = branches
|
||||
ctx.Data["CommitStatus"] = commitStatus
|
||||
ctx.Data["CommitStatuses"] = commitStatuses
|
||||
ctx.Data["DefaultBranchBranch"] = defaultBranch
|
||||
ctx.Data["DefaultBranchBranch"] = defaultBranchOptional
|
||||
pager := context.NewPagination(branchesCount, pageSize, page, 5)
|
||||
pager.AddParamFromRequest(ctx.Req)
|
||||
ctx.Data["Page"] = pager
|
||||
@@ -152,7 +155,7 @@ func RestoreBranchPost(ctx *context.Context) {
|
||||
objectFormat := git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName)
|
||||
|
||||
// Don't return error below this
|
||||
if err := repo_service.PushUpdate(
|
||||
if err := repo_service.PushUpdates(
|
||||
&repo_module.PushUpdateOptions{
|
||||
RefFullName: git.RefNameFromBranch(deletedBranch.Name),
|
||||
OldCommitID: objectFormat.EmptyObjectID().String(),
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/cache"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/optional"
|
||||
@@ -63,22 +62,6 @@ func MustBeAbleToUpload(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func CommitInfoCache(ctx *context.Context) {
|
||||
var err error
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitsCount", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||
ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
|
||||
}
|
||||
|
||||
func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
|
||||
orgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
|
||||
+2
-4
@@ -1476,15 +1476,13 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", web.Bind(forms.NewReleaseForm{}), repo.NewReleasePost)
|
||||
m.Get("/edit/*", repo.EditRelease)
|
||||
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
|
||||
m.Post("/generate-notes", web.Bind(forms.GenerateReleaseNotesForm{}), repo.GenerateReleaseNotes)
|
||||
m.Post("/delete", repo.DeleteRelease)
|
||||
m.Post("/attachments", repo.UploadReleaseAttachment)
|
||||
m.Post("/attachments/remove", repo.DeleteAttachment)
|
||||
}, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter)
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/edit/*", repo.EditRelease)
|
||||
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
|
||||
}, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
|
||||
}, optSignIn, context.RepoAssignment, repo.MustBeNotEmpty, reqRepoReleaseReader)
|
||||
// end "/{username}/{reponame}": repo releases
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.dev/modules/graceful"
|
||||
"gitea.dev/modules/private"
|
||||
"gitea.dev/modules/process"
|
||||
"gitea.dev/modules/web"
|
||||
web_types "gitea.dev/modules/web/types"
|
||||
@@ -49,6 +50,14 @@ func (ctx *PrivateContext) Err() error {
|
||||
return ctx.Base.Err()
|
||||
}
|
||||
|
||||
func (ctx *PrivateContext) PrivateError(status int, err error, userMsg string) {
|
||||
errMsg := ""
|
||||
if err != nil {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
ctx.JSON(status, private.Response{Err: errMsg, UserMsg: userMsg})
|
||||
}
|
||||
|
||||
type privateContextKeyType struct{}
|
||||
|
||||
var privateContextKey privateContextKeyType
|
||||
|
||||
@@ -972,12 +972,9 @@ func RepoRefByType(detectRefType git.RefType) func(*Context) {
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refShortName)
|
||||
if err == nil {
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") {
|
||||
} else {
|
||||
// if the repository is broken, we can continue to the handler code, to show "Settings -> Delete Repository" for end users
|
||||
log.Error("GetBranchCommit: %v", err)
|
||||
} else {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
return
|
||||
}
|
||||
} else { // there is a path in request
|
||||
guessLegacyPath := refType == ""
|
||||
|
||||
@@ -59,9 +59,9 @@ type Branch struct {
|
||||
}
|
||||
|
||||
// LoadBranches loads branches from the repository limited by page & pageSize.
|
||||
func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, isDeletedBranch optional.Option[bool], keyword string, page, pageSize int) (*Branch, []*Branch, int64, error) {
|
||||
defaultDBBranch, err := git_model.GetBranch(ctx, repo.ID, repo.DefaultBranch)
|
||||
if err != nil {
|
||||
func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, isDeletedBranch optional.Option[bool], keyword string, page, pageSize int) (defaultBranchOptional *Branch, _ []*Branch, _ int64, _ error) {
|
||||
defaultDBBranchOptional, err := git_model.GetBranch(ctx, repo.ID, repo.DefaultBranch)
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
@@ -108,13 +108,14 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git
|
||||
branches = append(branches, branch)
|
||||
}
|
||||
|
||||
if defaultDBBranchOptional != nil {
|
||||
// Always add the default branch
|
||||
log.Debug("loadOneBranch: load default: '%s'", defaultDBBranch.Name)
|
||||
defaultBranch, err := loadOneBranch(ctx, repo, defaultDBBranch, &rules, repoIDToRepo, repoIDToGitRepo)
|
||||
defaultBranchOptional, err = loadOneBranch(ctx, repo, defaultDBBranchOptional, &rules, repoIDToRepo, repoIDToGitRepo)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("loadOneBranch: %v", err)
|
||||
}
|
||||
return defaultBranch, branches, totalNumOfBranches, nil
|
||||
}
|
||||
return defaultBranchOptional, branches, totalNumOfBranches, nil
|
||||
}
|
||||
|
||||
func getDivergenceCacheKey(repoID int64, branchName string) string {
|
||||
@@ -640,7 +641,7 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
|
||||
|
||||
func deleteBranchSuccessPostProcess(doer *user_model.User, repo *repo_model.Repository, branchName string, branchCommit *git.Commit) {
|
||||
objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName)
|
||||
if err := PushUpdate(
|
||||
if err := PushUpdates(
|
||||
&repo_module.PushUpdateOptions{
|
||||
RefFullName: git.RefNameFromBranch(branchName),
|
||||
OldCommitID: branchCommit.ID.String(),
|
||||
|
||||
+29
-38
@@ -32,10 +32,26 @@ import (
|
||||
// pushQueue represents a queue to handle update pull request tests
|
||||
var pushQueue *queue.WorkerPoolQueue[[]*repo_module.PushUpdateOptions]
|
||||
|
||||
// handle passed PR IDs and test the PRs
|
||||
func handler(items ...[]*repo_module.PushUpdateOptions) [][]*repo_module.PushUpdateOptions {
|
||||
func initPushQueue() error {
|
||||
pushQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "push_update", pushQueueHandler)
|
||||
if pushQueue == nil {
|
||||
return errors.New("unable to create push_update queue")
|
||||
}
|
||||
go graceful.GetManager().RunWithCancel(pushQueue)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushUpdates adds a push update to push queue, each call must pass the same repo updates
|
||||
func PushUpdates(opts ...*repo_module.PushUpdateOptions) error {
|
||||
if len(opts) == 0 {
|
||||
return nil
|
||||
}
|
||||
return pushQueue.Push(opts)
|
||||
}
|
||||
|
||||
func pushQueueHandler(items ...[]*repo_module.PushUpdateOptions) [][]*repo_module.PushUpdateOptions {
|
||||
for _, opts := range items {
|
||||
if err := pushUpdates(opts); err != nil {
|
||||
if err := pushQueueHandleUpdates(opts); err != nil {
|
||||
// Username and repository stays the same between items in opts.
|
||||
pushUpdate := opts[0]
|
||||
log.Error("pushUpdate[%s/%s] failed: %v", pushUpdate.RepoUserName, pushUpdate.RepoName, err)
|
||||
@@ -44,37 +60,8 @@ func handler(items ...[]*repo_module.PushUpdateOptions) [][]*repo_module.PushUpd
|
||||
return nil
|
||||
}
|
||||
|
||||
func initPushQueue() error {
|
||||
pushQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "push_update", handler)
|
||||
if pushQueue == nil {
|
||||
return errors.New("unable to create push_update queue")
|
||||
}
|
||||
go graceful.GetManager().RunWithCancel(pushQueue)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushUpdate is an alias of PushUpdates for single push update options
|
||||
func PushUpdate(opts *repo_module.PushUpdateOptions) error {
|
||||
return PushUpdates([]*repo_module.PushUpdateOptions{opts})
|
||||
}
|
||||
|
||||
// PushUpdates adds a push update to push queue
|
||||
func PushUpdates(opts []*repo_module.PushUpdateOptions) error {
|
||||
if len(opts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if opt.IsNewRef() && opt.IsDelRef() {
|
||||
return errors.New("Old and new revisions are both NULL")
|
||||
}
|
||||
}
|
||||
|
||||
return pushQueue.Push(opts)
|
||||
}
|
||||
|
||||
// pushUpdates generates push action history feeds for push updating multiple refs
|
||||
func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
// pushQueueHandleUpdates generates push action history feeds for push updating multiple refs
|
||||
func pushQueueHandleUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
if len(optsList) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -94,7 +81,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
defer gitRepo.Close()
|
||||
|
||||
if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
|
||||
return fmt.Errorf("Failed to update size for repository: %v", err)
|
||||
return fmt.Errorf("failed to update size for repository: %v", err)
|
||||
}
|
||||
|
||||
addTags := make([]string, 0, len(optsList))
|
||||
@@ -104,10 +91,11 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
|
||||
for _, opts := range optsList {
|
||||
log.Trace("pushUpdates: %-v %s %s %s", repo, opts.OldCommitID, opts.NewCommitID, opts.RefFullName)
|
||||
|
||||
if opts.IsNewRef() && opts.IsDelRef() {
|
||||
return fmt.Errorf("old and new revisions are both %s", objectFormat.EmptyObjectID())
|
||||
setting.PanicInDevOrTesting("invalid push update (add+del): %+v", opts)
|
||||
continue
|
||||
}
|
||||
|
||||
if opts.RefFullName.IsTag() {
|
||||
if pusher == nil || pusher.ID != opts.PusherID {
|
||||
if opts.PusherID == user_model.ActionsUserID {
|
||||
@@ -188,11 +176,14 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// delete cache for divergence
|
||||
// sync branch related database data
|
||||
if branch == repo.DefaultBranch {
|
||||
if err := DelRepoDivergenceFromCache(ctx, repo.ID); err != nil {
|
||||
log.Error("DelRepoDivergenceFromCache: %v", err)
|
||||
}
|
||||
if err := AddRepoToLicenseUpdaterQueue(&LicenseUpdaterOptions{RepoID: repo.ID}); err != nil {
|
||||
log.Error("AddRepoToLicenseUpdaterQueue: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err := DelDivergenceFromCache(repo.ID, branch); err != nil {
|
||||
log.Error("DelDivergenceFromCache: %v", err)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
{{template "repo/sub_menu" .}}
|
||||
{{if .DefaultBranchBranch}}
|
||||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "repo.default_branch"}}
|
||||
{{if and $.IsWriter $.Repository.CanContentChange (not .IsDeleted)}}
|
||||
@@ -14,7 +13,8 @@
|
||||
{{end}}
|
||||
</h4>
|
||||
|
||||
<div class="ui attached table segment">
|
||||
{{if .DefaultBranchBranch}}
|
||||
<div class="ui attached table segment" data-testid="branches-default-branch-list">
|
||||
<table class="ui very basic fixed table single line">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -69,6 +69,10 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="ui attached segment" data-testid="branches-default-branch-not-exist">
|
||||
{{ctx.Locale.Tr "repo.branch.default_branch_not_exist" $.Repository.DefaultBranch}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h4 class="ui top attached header">
|
||||
|
||||
@@ -8,6 +8,9 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/models/db"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/translation"
|
||||
"gitea.dev/tests"
|
||||
|
||||
@@ -18,40 +21,24 @@ import (
|
||||
func TestViewBranches(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
req := NewRequest(t, "GET", "/user2/repo1/branches")
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
_, exists := htmlDoc.doc.Find(".delete-branch-button").Attr("data-url")
|
||||
assert.False(t, exists, "The template has changed")
|
||||
}
|
||||
AssertHTMLElement(t, htmlDoc, "[data-testid=branches-default-branch-list]", 1)
|
||||
AssertHTMLElement(t, htmlDoc, "[data-testid=branches-default-branch-not-exist]", 0)
|
||||
|
||||
func TestDeleteBranch(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
deleteBranch(t)
|
||||
repo1.DefaultBranch = "non-existent-branch"
|
||||
_, _ = db.GetEngine(t.Context()).ID(repo1.ID).Cols("default_branch").Update(repo1)
|
||||
req = NewRequest(t, "GET", "/user2/repo1/branches")
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||
AssertHTMLElement(t, htmlDoc, "[data-testid=branches-default-branch-list]", 0)
|
||||
AssertHTMLElement(t, htmlDoc, "[data-testid=branches-default-branch-not-exist]", 1)
|
||||
}
|
||||
|
||||
func TestUndoDeleteBranch(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
deleteBranch(t)
|
||||
htmlDoc, name := branchAction(t, ".restore-branch-button")
|
||||
assert.Contains(t,
|
||||
htmlDoc.doc.Find(".ui.positive.message").Text(),
|
||||
translation.NewLocale("en-US").TrString("repo.branch.restore_success", name),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func deleteBranch(t *testing.T) {
|
||||
htmlDoc, name := branchAction(t, ".delete-branch-button")
|
||||
assert.Contains(t,
|
||||
htmlDoc.doc.Find(".ui.positive.message").Text(),
|
||||
translation.NewLocale("en-US").TrString("repo.branch.deletion_success", name),
|
||||
)
|
||||
}
|
||||
|
||||
func branchAction(t *testing.T, button string) (*HTMLDoc, string) {
|
||||
branchAction := func(t *testing.T, button string) (*HTMLDoc, string) {
|
||||
session := loginUser(t, "user2")
|
||||
req := NewRequest(t, "GET", "/user2/repo1/branches")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
@@ -59,14 +46,27 @@ func branchAction(t *testing.T, button string) (*HTMLDoc, string) {
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
link, exists := htmlDoc.doc.Find(button).Attr("data-url")
|
||||
require.True(t, exists, "The template has changed")
|
||||
linkURL, err := url.Parse(link)
|
||||
require.NoError(t, err)
|
||||
|
||||
req = NewRequest(t, "POST", link)
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
url, err := url.Parse(link)
|
||||
assert.NoError(t, err)
|
||||
req = NewRequest(t, "GET", "/user2/repo1/branches")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
return NewHTMLParser(t, resp.Body), url.Query().Get("name")
|
||||
return NewHTMLParser(t, resp.Body), linkURL.Query().Get("name")
|
||||
}
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
htmlDoc, name := branchAction(t, ".delete-branch-button")
|
||||
assert.Contains(t,
|
||||
htmlDoc.doc.Find(".ui.positive.message").Text(),
|
||||
translation.NewLocale("en-US").TrString("repo.branch.deletion_success", name),
|
||||
)
|
||||
htmlDoc, name = branchAction(t, ".restore-branch-button")
|
||||
assert.Contains(t,
|
||||
htmlDoc.doc.Find(".ui.positive.message").Text(),
|
||||
translation.NewLocale("en-US").TrString("repo.branch.restore_success", name),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user