mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Fix git error handling (#32401)
This commit is contained in:
		| @@ -4,28 +4,14 @@ | ||||
| package git | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"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 | ||||
| type ErrNotExist struct { | ||||
| 	ID      string | ||||
| @@ -62,21 +48,6 @@ func IsErrBadLink(err error) bool { | ||||
| 	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. | ||||
| type ErrBranchNotExist struct { | ||||
| 	Name string | ||||
| @@ -185,3 +156,10 @@ func IsErrMoreThanOne(err error) bool { | ||||
| func (err *ErrMoreThanOne) Error() string { | ||||
| 	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") | ||||
| } | ||||
|   | ||||
| @@ -166,9 +166,7 @@ func (c *CheckAttributeReader) Run() error { | ||||
| 		Stdout: c.stdOut, | ||||
| 		Stderr: stdErr, | ||||
| 	}) | ||||
| 	if err != nil && //                      If there is an error we need to return but: | ||||
| 		c.ctx.Err() != err && //             1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded) | ||||
| 		err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed | ||||
| 	if err != nil && !IsErrCanceledOrKilled(err) { | ||||
| 		return fmt.Errorf("failed to run attr-check. Error: %w\nStderr: %s", err, stdErr.String()) | ||||
| 	} | ||||
| 	return nil | ||||
|   | ||||
| @@ -21,7 +21,6 @@ import ( | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	unit_model "code.gitea.io/gitea/models/unit" | ||||
| 	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/label" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| @@ -739,11 +738,9 @@ 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 !repo.IsEmpty { | ||||
| 			if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil { | ||||
| 				if !git.IsErrUnsupportedVersion(err) { | ||||
| 				ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err) | ||||
| 				return err | ||||
| 			} | ||||
| 			} | ||||
| 			updateRepoLicense = true | ||||
| 		} | ||||
| 		repo.DefaultBranch = *opts.DefaultBranch | ||||
|   | ||||
| @@ -8,7 +8,6 @@ import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	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/private" | ||||
| 	gitea_context "code.gitea.io/gitea/services/context" | ||||
| @@ -23,13 +22,11 @@ func SetDefaultBranch(ctx *gitea_context.PrivateContext) { | ||||
|  | ||||
| 	ctx.Repo.Repository.DefaultBranch = branch | ||||
| 	if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil { | ||||
| 		if !git.IsErrUnsupportedVersion(err) { | ||||
| 		ctx.JSON(http.StatusInternalServerError, private.Response{ | ||||
| 			Err: fmt.Sprintf("Unable to set default branch on repository: %s/%s Error: %v", ownerName, repoName, err), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	if err := repo_model.UpdateDefaultBranch(ctx, ctx.Repo.Repository); err != nil { | ||||
| 		ctx.JSON(http.StatusInternalServerError, private.Response{ | ||||
|   | ||||
| @@ -467,7 +467,7 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) { | ||||
| 		Stderr:            &stderr, | ||||
| 		UseContextTimeout: true, | ||||
| 	}); 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()) | ||||
| 		} | ||||
| 		return | ||||
|   | ||||
| @@ -1162,7 +1162,7 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi | ||||
| 			Dir:     repoPath, | ||||
| 			Stdout:  writer, | ||||
| 			Stderr:  stderr, | ||||
| 		}); err != nil && err.Error() != "signal: killed" { | ||||
| 		}); err != nil && !git.IsErrCanceledOrKilled(err) { | ||||
| 			log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String()) | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -613,15 +613,9 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, re | ||||
| 		} | ||||
| 		// Update the git repository default branch | ||||
| 		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) | ||||
| 				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 | ||||
| 		} | ||||
| 		} | ||||
| 		m.Repo.IsEmpty = false | ||||
| 		// Update the is empty and default_branch columns | ||||
| 		if err := repo_model.UpdateRepositoryCols(ctx, m.Repo, "default_branch", "is_empty"); err != nil { | ||||
|   | ||||
| @@ -602,12 +602,7 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR | ||||
| 			log.Error("CancelPreviousJobs: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		if err := gitrepo.SetDefaultBranch(ctx, repo, newBranchName); err != nil { | ||||
| 			if !git.IsErrUnsupportedVersion(err) { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
| 		return gitrepo.SetDefaultBranch(ctx, repo, newBranchName) | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
| @@ -339,8 +339,7 @@ func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash, bran | ||||
| func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { | ||||
| 	stdoutReader, stdoutWriter, err := os.Pipe() | ||||
| 	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() { | ||||
| 		_ = stdoutReader.Close() | ||||
| @@ -348,9 +347,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { | ||||
| 	}() | ||||
| 	stderr := new(bytes.Buffer) | ||||
| 	var diff *gitdiff.Diff | ||||
| 	var finalErr error | ||||
|  | ||||
| 	if err := git.NewCommand(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). | ||||
| 	err = git.NewCommand(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Timeout: 30 * time.Second, | ||||
| 			Dir:     t.basePath, | ||||
| @@ -359,27 +356,19 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { | ||||
| 			PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { | ||||
| 				_ = stdoutWriter.Close() | ||||
| 				defer cancel() | ||||
| 				diff, finalErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "") | ||||
| 				if finalErr != nil { | ||||
| 					log.Error("ParsePatch: %v", finalErr) | ||||
| 					cancel() | ||||
| 				} | ||||
| 				var diffErr error | ||||
| 				diff, diffErr = gitdiff.ParsePatch(t.ctx, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "") | ||||
| 				_ = stdoutReader.Close() | ||||
| 				return finalErr | ||||
| 				if diffErr != nil { | ||||
| 					// if the diffErr is not nil, it will be returned as the error of "Run()" | ||||
| 					return fmt.Errorf("ParsePatch: %w", diffErr) | ||||
| 				} | ||||
| 				return nil | ||||
| 			}, | ||||
| 		}); err != nil { | ||||
| 		if finalErr != nil { | ||||
| 			log.Error("Unable to ParsePatch in temporary repo %s (%s). Error: %v", t.repo.FullName(), t.basePath, finalErr) | ||||
| 			return nil, finalErr | ||||
| 		} | ||||
|  | ||||
| 		// If the process exited early, don't error | ||||
| 		if err != context.Canceled { | ||||
| 			log.Error("Unable to run diff-index pipeline 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 %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") | ||||
|   | ||||
| @@ -183,11 +183,9 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { | ||||
| 						repo.IsEmpty = false | ||||
| 						if repo.DefaultBranch != setting.Repository.DefaultBranch { | ||||
| 							if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { | ||||
| 								if !git.IsErrUnsupportedVersion(err) { | ||||
| 								return err | ||||
| 							} | ||||
| 						} | ||||
| 						} | ||||
| 						// Update the is empty and default_branch columns | ||||
| 						if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_branch", "is_empty"); err != nil { | ||||
| 							return fmt.Errorf("UpdateRepositoryCols: %w", err) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user