[PORT] Fix git error handling (gitea#32401)
--- Conflict resolution: Trivial, for `repo_attributes.go` move where the `IsErrCanceledOrKilled` needs to happen because of other changes that happened in this file. To add some words to this change: It seems to be mostly simplifying the error handling of git operations. (cherry picked from commit e524f63d58900557d7d57fc3bcd19d9facc8b8ee)
This commit is contained in:
parent
20c0a2a381
commit
171de4d107
11 changed files with 36 additions and 86 deletions
|
@ -137,9 +137,6 @@ code.gitea.io/gitea/modules/git
|
||||||
AddChangesWithArgs
|
AddChangesWithArgs
|
||||||
CommitChanges
|
CommitChanges
|
||||||
CommitChangesWithArgs
|
CommitChangesWithArgs
|
||||||
IsErrExecTimeout
|
|
||||||
ErrExecTimeout.Error
|
|
||||||
ErrUnsupportedVersion.Error
|
|
||||||
SetUpdateHook
|
SetUpdateHook
|
||||||
openRepositoryWithDefaultContext
|
openRepositoryWithDefaultContext
|
||||||
IsTagExist
|
IsTagExist
|
||||||
|
|
|
@ -4,28 +4,14 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrExecTimeout error when exec timed out
|
|
||||||
type ErrExecTimeout struct {
|
|
||||||
Duration time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrExecTimeout if some error is ErrExecTimeout
|
|
||||||
func IsErrExecTimeout(err error) bool {
|
|
||||||
_, ok := err.(ErrExecTimeout)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrExecTimeout) Error() string {
|
|
||||||
return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrNotExist commit not exist error
|
// ErrNotExist commit not exist error
|
||||||
type ErrNotExist struct {
|
type ErrNotExist struct {
|
||||||
ID string
|
ID string
|
||||||
|
@ -62,21 +48,6 @@ func IsErrBadLink(err error) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUnsupportedVersion error when required git version not matched
|
|
||||||
type ErrUnsupportedVersion struct {
|
|
||||||
Required string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrUnsupportedVersion if some error is ErrUnsupportedVersion
|
|
||||||
func IsErrUnsupportedVersion(err error) bool {
|
|
||||||
_, ok := err.(ErrUnsupportedVersion)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrUnsupportedVersion) Error() string {
|
|
||||||
return fmt.Sprintf("Operation requires higher version [required: %s]", err.Required)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
|
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
|
||||||
type ErrBranchNotExist struct {
|
type ErrBranchNotExist struct {
|
||||||
Name string
|
Name string
|
||||||
|
@ -185,3 +156,10 @@ func IsErrMoreThanOne(err error) bool {
|
||||||
func (err *ErrMoreThanOne) Error() string {
|
func (err *ErrMoreThanOne) Error() string {
|
||||||
return fmt.Sprintf("ErrMoreThanOne Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
return fmt.Sprintf("ErrMoreThanOne Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsErrCanceledOrKilled(err error) bool {
|
||||||
|
// When "cancel()" a git command's context, the returned error of "Run()" could be one of them:
|
||||||
|
// - context.Canceled
|
||||||
|
// - *exec.ExitError: "signal: killed"
|
||||||
|
return err != nil && (errors.Is(err, context.Canceled) || err.Error() == "signal: killed")
|
||||||
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ func (repo *Repository) GitAttributeChecker(treeish string, attributes ...string
|
||||||
err = e
|
err = e
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil { // decorate the returned error
|
if err != nil && !IsErrCanceledOrKilled(err) { // decorate the returned error
|
||||||
err = fmt.Errorf("git check-attr (stderr: %q): %w", strings.TrimSpace(stdErr.String()), err)
|
err = fmt.Errorf("git check-attr (stderr: %q): %w", strings.TrimSpace(stdErr.String()), err)
|
||||||
ac.err.Store(err)
|
ac.err.Store(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
unit_model "code.gitea.io/gitea/models/unit"
|
unit_model "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/git"
|
|
||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/label"
|
"code.gitea.io/gitea/modules/label"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -748,12 +747,10 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
||||||
if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) {
|
if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) {
|
||||||
if !repo.IsEmpty {
|
if !repo.IsEmpty {
|
||||||
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil {
|
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil {
|
||||||
if !git.IsErrUnsupportedVersion(err) {
|
|
||||||
ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err)
|
ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
repo.DefaultBranch = *opts.DefaultBranch
|
repo.DefaultBranch = *opts.DefaultBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/git"
|
|
||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
gitea_context "code.gitea.io/gitea/services/context"
|
gitea_context "code.gitea.io/gitea/services/context"
|
||||||
|
@ -22,13 +21,11 @@ func SetDefaultBranch(ctx *gitea_context.PrivateContext) {
|
||||||
|
|
||||||
ctx.Repo.Repository.DefaultBranch = branch
|
ctx.Repo.Repository.DefaultBranch = branch
|
||||||
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
|
if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
|
||||||
if !git.IsErrUnsupportedVersion(err) {
|
|
||||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||||
Err: fmt.Sprintf("Unable to set default branch on repository: %s/%s Error: %v", ownerName, repoName, err),
|
Err: fmt.Sprintf("Unable to set default branch on repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := repo_model.UpdateDefaultBranch(ctx, ctx.Repo.Repository); err != nil {
|
if err := repo_model.UpdateDefaultBranch(ctx, ctx.Repo.Repository); err != nil {
|
||||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||||
|
|
|
@ -467,7 +467,7 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) {
|
||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
UseContextTimeout: true,
|
UseContextTimeout: true,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
if err.Error() != "signal: killed" {
|
if !git.IsErrCanceledOrKilled(err) {
|
||||||
log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.getRepoDir(), err, stderr.String())
|
log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.getRepoDir(), err, stderr.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
@ -1159,7 +1159,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
|
||||||
Dir: repoPath,
|
Dir: repoPath,
|
||||||
Stdout: writer,
|
Stdout: writer,
|
||||||
Stderr: stderr,
|
Stderr: stderr,
|
||||||
}); err != nil {
|
}); err != nil && !git.IsErrCanceledOrKilled(err) {
|
||||||
log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String())
|
log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -604,15 +604,9 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, re
|
||||||
}
|
}
|
||||||
// Update the git repository default branch
|
// Update the git repository default branch
|
||||||
if err := gitrepo.SetDefaultBranch(ctx, m.Repo, m.Repo.DefaultBranch); err != nil {
|
if err := gitrepo.SetDefaultBranch(ctx, m.Repo, m.Repo.DefaultBranch); err != nil {
|
||||||
if !git.IsErrUnsupportedVersion(err) {
|
|
||||||
log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err)
|
log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err)
|
||||||
desc := fmt.Sprintf("Failed to update default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err)
|
|
||||||
if err = system_model.CreateRepositoryNotice(desc); err != nil {
|
|
||||||
log.Error("CreateRepositoryNotice: %v", err)
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
m.Repo.IsEmpty = false
|
m.Repo.IsEmpty = false
|
||||||
// Update the is empty and default_branch columns
|
// Update the is empty and default_branch columns
|
||||||
if err := repo_model.UpdateRepositoryCols(ctx, m.Repo, "default_branch", "is_empty"); err != nil {
|
if err := repo_model.UpdateRepositoryCols(ctx, m.Repo, "default_branch", "is_empty"); err != nil {
|
||||||
|
|
|
@ -588,12 +588,7 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR
|
||||||
log.Error("CancelPreviousJobs: %v", err)
|
log.Error("CancelPreviousJobs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gitrepo.SetDefaultBranch(ctx, repo, newBranchName); err != nil {
|
return gitrepo.SetDefaultBranch(ctx, repo, newBranchName)
|
||||||
if !git.IsErrUnsupportedVersion(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -343,8 +343,7 @@ func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash, bran
|
||||||
func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
|
func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
|
||||||
stdoutReader, stdoutWriter, err := os.Pipe()
|
stdoutReader, stdoutWriter, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to open stdout pipe: %v", err)
|
return nil, fmt.Errorf("unable to open stdout pipe: %w", err)
|
||||||
return nil, fmt.Errorf("Unable to open stdout pipe: %w", err)
|
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = stdoutReader.Close()
|
_ = stdoutReader.Close()
|
||||||
|
@ -352,9 +351,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
|
||||||
}()
|
}()
|
||||||
stderr := new(bytes.Buffer)
|
stderr := new(bytes.Buffer)
|
||||||
var diff *gitdiff.Diff
|
var diff *gitdiff.Diff
|
||||||
var finalErr error
|
err = git.NewCommand(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
|
||||||
|
|
||||||
if err := git.NewCommand(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
|
|
||||||
Run(&git.RunOpts{
|
Run(&git.RunOpts{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
Dir: t.basePath,
|
Dir: t.basePath,
|
||||||
|
@ -362,23 +359,20 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
|
||||||
Stderr: stderr,
|
Stderr: stderr,
|
||||||
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
|
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
|
||||||
_ = stdoutWriter.Close()
|
_ = stdoutWriter.Close()
|
||||||
diff, finalErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
|
defer cancel()
|
||||||
if finalErr != nil {
|
var diffErr error
|
||||||
log.Error("ParsePatch: %v", finalErr)
|
diff, diffErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "")
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
_ = stdoutReader.Close()
|
_ = stdoutReader.Close()
|
||||||
return finalErr
|
if diffErr != nil {
|
||||||
},
|
// if the diffErr is not nil, it will be returned as the error of "Run()"
|
||||||
}); err != nil {
|
return fmt.Errorf("ParsePatch: %w", diffErr)
|
||||||
if finalErr != nil {
|
|
||||||
log.Error("Unable to ParsePatch in temporary repo %s (%s). Error: %v", t.repo.FullName(), t.basePath, finalErr)
|
|
||||||
return nil, finalErr
|
|
||||||
}
|
}
|
||||||
log.Error("Unable to run diff-index pipeline in temporary repo %s (%s). Error: %v\nStderr: %s",
|
return nil
|
||||||
t.repo.FullName(), t.basePath, err, stderr)
|
},
|
||||||
return nil, fmt.Errorf("Unable to run diff-index pipeline in temporary repo %s. Error: %w\nStderr: %s",
|
})
|
||||||
t.repo.FullName(), err, stderr)
|
if err != nil && !git.IsErrCanceledOrKilled(err) {
|
||||||
|
log.Error("Unable to diff-index in temporary repo %s (%s). Error: %v\nStderr: %s", t.repo.FullName(), t.basePath, err, stderr)
|
||||||
|
return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.ctx, t.basePath, git.TrustedCmdArgs{"--cached"}, "HEAD")
|
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.ctx, t.basePath, git.TrustedCmdArgs{"--cached"}, "HEAD")
|
||||||
|
|
|
@ -183,11 +183,9 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||||
repo.IsEmpty = false
|
repo.IsEmpty = false
|
||||||
if repo.DefaultBranch != setting.Repository.DefaultBranch {
|
if repo.DefaultBranch != setting.Repository.DefaultBranch {
|
||||||
if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
|
if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil {
|
||||||
if !git.IsErrUnsupportedVersion(err) {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Update the is empty and default_branch columns
|
// Update the is empty and default_branch columns
|
||||||
if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_branch", "is_empty"); err != nil {
|
if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_branch", "is_empty"); err != nil {
|
||||||
return fmt.Errorf("UpdateRepositoryCols: %w", err)
|
return fmt.Errorf("UpdateRepositoryCols: %w", err)
|
||||||
|
|
Loading…
Add table
Reference in a new issue