1
0
mirror of https://github.com/go-gitea/gitea.git synced 2024-09-01 14:56:30 +00:00

Make Requests Processes and create process hierarchy. Associate OpenRepository with context. ()

This PR registers requests with the process manager and manages hierarchy within the processes.

Git repos are then associated with a context, (usually the request's context) - with sub commands using this context as their base context.

Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
zeripath 2021-11-30 20:06:32 +00:00 committed by GitHub
parent d894c90b70
commit 01087e9eef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 591 additions and 306 deletions

View File

@ -309,7 +309,7 @@ func runHookPostReceive(c *cli.Context) error {
defer cancel() defer cancel()
// First of all run update-server-info no matter what // First of all run update-server-info no matter what
if _, err := git.NewCommand("update-server-info").SetParentContext(ctx).Run(); err != nil { if _, err := git.NewCommandContext(ctx, "update-server-info").Run(); err != nil {
return fmt.Errorf("Failed to call 'git update-server-info': %v", err) return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
} }

View File

@ -534,7 +534,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
return return
} }
gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName)) gitRepo, err := git.OpenRepositoryCtx(ctx, models.RepoPath(userName, repoName))
if err != nil { if err != nil {
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") { if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err) log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
@ -792,7 +792,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
if ctx.Repo.GitRepo == nil { if ctx.Repo.GitRepo == nil {
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath) ctx.Repo.GitRepo, err = git.OpenRepositoryCtx(ctx, repoPath)
if err != nil { if err != nil {
ctx.ServerError("RepoRef Invalid repo "+repoPath, err) ctx.ServerError("RepoRef Invalid repo "+repoPath, err)
return return

View File

@ -28,17 +28,15 @@ type WriteCloserError interface {
} }
// CatFileBatchCheck opens git cat-file --batch-check in the provided repo and returns a stdin pipe, a stdout reader and cancel function // CatFileBatchCheck opens git cat-file --batch-check in the provided repo and returns a stdin pipe, a stdout reader and cancel function
func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) { func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError, *bufio.Reader, func()) {
batchStdinReader, batchStdinWriter := io.Pipe() batchStdinReader, batchStdinWriter := io.Pipe()
batchStdoutReader, batchStdoutWriter := io.Pipe() batchStdoutReader, batchStdoutWriter := io.Pipe()
ctx, ctxCancel := context.WithCancel(DefaultContext) ctx, ctxCancel := context.WithCancel(ctx)
closed := make(chan struct{}) closed := make(chan struct{})
cancel := func() { cancel := func() {
_ = batchStdinReader.Close()
_ = batchStdinWriter.Close()
_ = batchStdoutReader.Close()
_ = batchStdoutWriter.Close()
ctxCancel() ctxCancel()
_ = batchStdoutReader.Close()
_ = batchStdinWriter.Close()
<-closed <-closed
} }
@ -67,19 +65,17 @@ func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()
} }
// CatFileBatch opens git cat-file --batch in the provided repo and returns a stdin pipe, a stdout reader and cancel function // CatFileBatch opens git cat-file --batch in the provided repo and returns a stdin pipe, a stdout reader and cancel function
func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) { func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufio.Reader, func()) {
// We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary. // We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
// so let's create a batch stdin and stdout // so let's create a batch stdin and stdout
batchStdinReader, batchStdinWriter := io.Pipe() batchStdinReader, batchStdinWriter := io.Pipe()
batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024)) batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024))
ctx, ctxCancel := context.WithCancel(DefaultContext) ctx, ctxCancel := context.WithCancel(ctx)
closed := make(chan struct{}) closed := make(chan struct{})
cancel := func() { cancel := func() {
_ = batchStdinReader.Close() ctxCancel()
_ = batchStdinWriter.Close() _ = batchStdinWriter.Close()
_ = batchStdoutReader.Close() _ = batchStdoutReader.Close()
_ = batchStdoutWriter.Close()
ctxCancel()
<-closed <-closed
} }

View File

@ -24,12 +24,12 @@ type BlamePart struct {
// BlameReader returns part of file blame one by one // BlameReader returns part of file blame one by one
type BlameReader struct { type BlameReader struct {
cmd *exec.Cmd cmd *exec.Cmd
pid int64 output io.ReadCloser
output io.ReadCloser reader *bufio.Reader
reader *bufio.Reader lastSha *string
lastSha *string cancel context.CancelFunc // Cancels the context that this reader runs in
cancel context.CancelFunc finished process.FinishedFunc // Tells the process manager we're finished and it can remove the associated process from the process table
} }
var shaLineRegex = regexp.MustCompile("^([a-z0-9]{40})") var shaLineRegex = regexp.MustCompile("^([a-z0-9]{40})")
@ -100,8 +100,8 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
// Close BlameReader - don't run NextPart after invoking that // Close BlameReader - don't run NextPart after invoking that
func (r *BlameReader) Close() error { func (r *BlameReader) Close() error {
defer process.GetManager().Remove(r.pid) defer r.finished() // Only remove the process from the process table when the underlying command is closed
r.cancel() r.cancel() // However, first cancel our own context early
_ = r.output.Close() _ = r.output.Close()
@ -114,7 +114,7 @@ func (r *BlameReader) Close() error {
// CreateBlameReader creates reader for given repository, commit and file // CreateBlameReader creates reader for given repository, commit and file
func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*BlameReader, error) { func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*BlameReader, error) {
gitRepo, err := OpenRepository(repoPath) gitRepo, err := OpenRepositoryCtx(ctx, repoPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -125,32 +125,31 @@ func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*B
func createBlameReader(ctx context.Context, dir string, command ...string) (*BlameReader, error) { func createBlameReader(ctx context.Context, dir string, command ...string) (*BlameReader, error) {
// Here we use the provided context - this should be tied to the request performing the blame so that it does not hang around. // Here we use the provided context - this should be tied to the request performing the blame so that it does not hang around.
ctx, cancel := context.WithCancel(ctx) ctx, cancel, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("GetBlame [repo_path: %s]", dir))
cmd := exec.CommandContext(ctx, command[0], command[1:]...) cmd := exec.CommandContext(ctx, command[0], command[1:]...)
cmd.Dir = dir cmd.Dir = dir
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
defer cancel() defer finished()
return nil, fmt.Errorf("StdoutPipe: %v", err) return nil, fmt.Errorf("StdoutPipe: %v", err)
} }
if err = cmd.Start(); err != nil { if err = cmd.Start(); err != nil {
defer cancel() defer finished()
_ = stdout.Close()
return nil, fmt.Errorf("Start: %v", err) return nil, fmt.Errorf("Start: %v", err)
} }
pid := process.GetManager().Add(fmt.Sprintf("GetBlame [repo_path: %s]", dir), cancel)
reader := bufio.NewReader(stdout) reader := bufio.NewReader(stdout)
return &BlameReader{ return &BlameReader{
cmd, cmd: cmd,
pid, output: stdout,
stdout, reader: reader,
reader, cancel: cancel,
nil, finished: finished,
cancel,
}, nil }, nil
} }

View File

@ -29,7 +29,7 @@ type Blob struct {
// DataAsync gets a ReadCloser for the contents of a blob without reading it all. // DataAsync gets a ReadCloser for the contents of a blob without reading it all.
// Calling the Close function on the result will discard all unread output. // Calling the Close function on the result will discard all unread output.
func (b *Blob) DataAsync() (io.ReadCloser, error) { func (b *Blob) DataAsync() (io.ReadCloser, error) {
wr, rd, cancel := b.repo.CatFileBatch() wr, rd, cancel := b.repo.CatFileBatch(b.repo.Ctx)
_, err := wr.Write([]byte(b.ID.String() + "\n")) _, err := wr.Write([]byte(b.ID.String() + "\n"))
if err != nil { if err != nil {
@ -67,7 +67,7 @@ func (b *Blob) Size() int64 {
return b.size return b.size
} }
wr, rd, cancel := b.repo.CatFileBatchCheck() wr, rd, cancel := b.repo.CatFileBatchCheck(b.repo.Ctx)
defer cancel() defer cancel()
_, err := wr.Write([]byte(b.ID.String() + "\n")) _, err := wr.Write([]byte(b.ID.String() + "\n"))
if err != nil { if err != nil {

View File

@ -143,8 +143,13 @@ func (c *Command) RunWithContext(rc *RunContext) error {
log.Debug("%s: %v", rc.Dir, c) log.Debug("%s: %v", rc.Dir, c)
} }
ctx, cancel := context.WithTimeout(c.parentContext, rc.Timeout) desc := c.desc
defer cancel() if desc == "" {
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(c.args, " "), rc.Dir)
}
ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc)
defer finished()
cmd := exec.CommandContext(ctx, c.name, c.args...) cmd := exec.CommandContext(ctx, c.name, c.args...)
if rc.Env == nil { if rc.Env == nil {
@ -172,13 +177,6 @@ func (c *Command) RunWithContext(rc *RunContext) error {
return err return err
} }
desc := c.desc
if desc == "" {
desc = fmt.Sprintf("%s %s %s [repo_path: %s]", GitExecutable, c.name, strings.Join(c.args, " "), rc.Dir)
}
pid := process.GetManager().Add(desc, cancel)
defer process.GetManager().Remove(pid)
if rc.PipelineFunc != nil { if rc.PipelineFunc != nil {
err := rc.PipelineFunc(ctx, cancel) err := rc.PipelineFunc(ctx, cancel)
if err != nil { if err != nil {

View File

@ -100,7 +100,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
} }
func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) { func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
wr, rd, cancel := cache.repo.CatFileBatch() wr, rd, cancel := cache.repo.CatFileBatch(ctx)
defer cancel() defer cancel()
var unHitEntryPaths []string var unHitEntryPaths []string
@ -129,7 +129,7 @@ func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, commit *
return nil, err return nil, err
} }
batchStdinWriter, batchReader, cancel := commit.repo.CatFileBatch() batchStdinWriter, batchReader, cancel := commit.repo.CatFileBatch(ctx)
defer cancel() defer cancel()
commitsMap := map[string]*Commit{} commitsMap := map[string]*Commit{}

View File

@ -56,8 +56,8 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
fileArgs = append(fileArgs, "--", file) fileArgs = append(fileArgs, "--", file)
} }
// FIXME: graceful: These commands should have a timeout // FIXME: graceful: These commands should have a timeout
ctx, cancel := context.WithCancel(DefaultContext) ctx, _, finished := process.GetManager().AddContext(repo.Ctx, fmt.Sprintf("GetRawDiffForFile: [repo_path: %s]", repo.Path))
defer cancel() defer finished()
var cmd *exec.Cmd var cmd *exec.Cmd
switch diffType { switch diffType {
@ -90,8 +90,6 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
cmd.Dir = repo.Path cmd.Dir = repo.Path
cmd.Stdout = writer cmd.Stdout = writer
cmd.Stderr = stderr cmd.Stderr = stderr
pid := process.GetManager().Add(fmt.Sprintf("GetRawDiffForFile: [repo_path: %s]", repo.Path), cancel)
defer process.GetManager().Remove(pid)
if err = cmd.Run(); err != nil { if err = cmd.Run(); err != nil {
return fmt.Errorf("Run: %v - %s", err, stderr) return fmt.Errorf("Run: %v - %s", err, stderr)

View File

@ -63,7 +63,7 @@ func FindLFSFile(repo *git.Repository, hash git.SHA1) ([]*LFSResult, error) {
// Next feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary. // Next feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
// so let's create a batch stdin and stdout // so let's create a batch stdin and stdout
batchStdinWriter, batchReader, cancel := repo.CatFileBatch() batchStdinWriter, batchReader, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel() defer cancel()
// We'll use a scanner for the revList because it's simpler than a bufio.Reader // We'll use a scanner for the revList because it's simpler than a bufio.Reader

View File

@ -4,19 +4,22 @@
package git package git
import "net/url" import (
"context"
"net/url"
)
// GetRemoteAddress returns the url of a specific remote of the repository. // GetRemoteAddress returns the url of a specific remote of the repository.
func GetRemoteAddress(repoPath, remoteName string) (*url.URL, error) { func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (*url.URL, error) {
err := LoadGitVersion() err := LoadGitVersion()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var cmd *Command var cmd *Command
if CheckGitVersionAtLeast("2.7") == nil { if CheckGitVersionAtLeast("2.7") == nil {
cmd = NewCommand("remote", "get-url", remoteName) cmd = NewCommandContext(ctx, "remote", "get-url", remoteName)
} else { } else {
cmd = NewCommand("config", "--get", "remote."+remoteName+".url") cmd = NewCommandContext(ctx, "config", "--get", "remote."+remoteName+".url")
} }
result, err := cmd.RunInDir(repoPath) result, err := cmd.RunInDir(repoPath)

View File

@ -211,8 +211,8 @@ type PushOptions struct {
} }
// Push pushs local commits to given remote branch. // Push pushs local commits to given remote branch.
func Push(repoPath string, opts PushOptions) error { func Push(ctx context.Context, repoPath string, opts PushOptions) error {
cmd := NewCommand("push") cmd := NewCommandContext(ctx, "push")
if opts.Force { if opts.Force {
cmd.AddArguments("-f") cmd.AddArguments("-f")
} }

View File

@ -74,7 +74,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
} }
} }
cmd := NewCommand(cmdArgs...) cmd := NewCommandContext(repo.Ctx, cmdArgs...)
if err := cmd.RunInDirTimeoutEnvPipeline(env, -1, repo.Path, stdOut, stdErr); err != nil { if err := cmd.RunInDirTimeoutEnvPipeline(env, -1, repo.Path, stdOut, stdErr); err != nil {
return nil, fmt.Errorf("failed to run check-attr: %v\n%s\n%s", err, stdOut.String(), stdErr.String()) return nil, fmt.Errorf("failed to run check-attr: %v\n%s\n%s", err, stdOut.String(), stdErr.String())

View File

@ -9,6 +9,7 @@
package git package git
import ( import (
"context"
"errors" "errors"
"path/filepath" "path/filepath"
@ -30,10 +31,17 @@ type Repository struct {
gogitRepo *gogit.Repository gogitRepo *gogit.Repository
gogitStorage *filesystem.Storage gogitStorage *filesystem.Storage
gpgSettings *GPGSettings gpgSettings *GPGSettings
Ctx context.Context
} }
// OpenRepository opens the repository at the given path. // OpenRepository opens the repository at the given path.
func OpenRepository(repoPath string) (*Repository, error) { func OpenRepository(repoPath string) (*Repository, error) {
return OpenRepositoryCtx(DefaultContext, repoPath)
}
// OpenRepositoryCtx opens the repository at the given path within the context.Context
func OpenRepositoryCtx(ctx context.Context, repoPath string) (*Repository, error) {
repoPath, err := filepath.Abs(repoPath) repoPath, err := filepath.Abs(repoPath)
if err != nil { if err != nil {
return nil, err return nil, err
@ -60,6 +68,7 @@ func OpenRepository(repoPath string) (*Repository, error) {
gogitRepo: gogitRepo, gogitRepo: gogitRepo,
gogitStorage: storage, gogitStorage: storage,
tagCache: newObjectCache(), tagCache: newObjectCache(),
Ctx: ctx,
}, nil }, nil
} }

View File

@ -32,10 +32,17 @@ type Repository struct {
checkCancel context.CancelFunc checkCancel context.CancelFunc
checkReader *bufio.Reader checkReader *bufio.Reader
checkWriter WriteCloserError checkWriter WriteCloserError
Ctx context.Context
} }
// OpenRepository opens the repository at the given path. // OpenRepository opens the repository at the given path.
func OpenRepository(repoPath string) (*Repository, error) { func OpenRepository(repoPath string) (*Repository, error) {
return OpenRepositoryCtx(DefaultContext, repoPath)
}
// OpenRepositoryCtx opens the repository at the given path with the provided context.
func OpenRepositoryCtx(ctx context.Context, repoPath string) (*Repository, error) {
repoPath, err := filepath.Abs(repoPath) repoPath, err := filepath.Abs(repoPath)
if err != nil { if err != nil {
return nil, err return nil, err
@ -46,28 +53,29 @@ func OpenRepository(repoPath string) (*Repository, error) {
repo := &Repository{ repo := &Repository{
Path: repoPath, Path: repoPath,
tagCache: newObjectCache(), tagCache: newObjectCache(),
Ctx: ctx,
} }
repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(repoPath) repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath)
repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(repo.Path) repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repo.Path)
return repo, nil return repo, nil
} }
// CatFileBatch obtains a CatFileBatch for this repository // CatFileBatch obtains a CatFileBatch for this repository
func (repo *Repository) CatFileBatch() (WriteCloserError, *bufio.Reader, func()) { func (repo *Repository) CatFileBatch(ctx context.Context) (WriteCloserError, *bufio.Reader, func()) {
if repo.batchCancel == nil || repo.batchReader.Buffered() > 0 { if repo.batchCancel == nil || repo.batchReader.Buffered() > 0 {
log.Debug("Opening temporary cat file batch for: %s", repo.Path) log.Debug("Opening temporary cat file batch for: %s", repo.Path)
return CatFileBatch(repo.Path) return CatFileBatch(ctx, repo.Path)
} }
return repo.batchWriter, repo.batchReader, func() {} return repo.batchWriter, repo.batchReader, func() {}
} }
// CatFileBatchCheck obtains a CatFileBatchCheck for this repository // CatFileBatchCheck obtains a CatFileBatchCheck for this repository
func (repo *Repository) CatFileBatchCheck() (WriteCloserError, *bufio.Reader, func()) { func (repo *Repository) CatFileBatchCheck(ctx context.Context) (WriteCloserError, *bufio.Reader, func()) {
if repo.checkCancel == nil || repo.checkReader.Buffered() > 0 { if repo.checkCancel == nil || repo.checkReader.Buffered() > 0 {
log.Debug("Opening temporary cat file batch-check: %s", repo.Path) log.Debug("Opening temporary cat file batch-check: %s", repo.Path)
return CatFileBatchCheck(repo.Path) return CatFileBatchCheck(ctx, repo.Path)
} }
return repo.checkWriter, repo.checkReader, func() {} return repo.checkWriter, repo.checkReader, func() {}
} }

View File

@ -8,12 +8,12 @@ import "fmt"
// FileBlame return the Blame object of file // FileBlame return the Blame object of file
func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) { func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) {
return NewCommand("blame", "--root", "--", file).RunInDirBytes(path) return NewCommandContext(repo.Ctx, "blame", "--root", "--", file).RunInDirBytes(path)
} }
// LineBlame returns the latest commit at the given line // LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) { func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
res, err := NewCommand("blame", fmt.Sprintf("-L %d,%d", line, line), "-p", revision, "--", file).RunInDir(path) res, err := NewCommandContext(repo.Ctx, "blame", fmt.Sprintf("-L %d,%d", line, line), "-p", revision, "--", file).RunInDir(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -6,6 +6,7 @@
package git package git
import ( import (
"context"
"fmt" "fmt"
"strings" "strings"
) )
@ -22,14 +23,14 @@ const PullRequestPrefix = "refs/for/"
// TODO: /refs/for-review for suggest change interface // TODO: /refs/for-review for suggest change interface
// IsReferenceExist returns true if given reference exists in the repository. // IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(repoPath, name string) bool { func IsReferenceExist(ctx context.Context, repoPath, name string) bool {
_, err := NewCommand("show-ref", "--verify", "--", name).RunInDir(repoPath) _, err := NewCommandContext(ctx, "show-ref", "--verify", "--", name).RunInDir(repoPath)
return err == nil return err == nil
} }
// IsBranchExist returns true if given branch exists in the repository. // IsBranchExist returns true if given branch exists in the repository.
func IsBranchExist(repoPath, name string) bool { func IsBranchExist(ctx context.Context, repoPath, name string) bool {
return IsReferenceExist(repoPath, BranchPrefix+name) return IsReferenceExist(ctx, repoPath, BranchPrefix+name)
} }
// Branch represents a Git branch. // Branch represents a Git branch.
@ -45,7 +46,7 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
if repo == nil { if repo == nil {
return nil, fmt.Errorf("nil repo") return nil, fmt.Errorf("nil repo")
} }
stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "symbolic-ref", "HEAD").RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -64,13 +65,13 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
// SetDefaultBranch sets default branch of repository. // SetDefaultBranch sets default branch of repository.
func (repo *Repository) SetDefaultBranch(name string) error { func (repo *Repository) SetDefaultBranch(name string) error {
_, err := NewCommand("symbolic-ref", "HEAD", BranchPrefix+name).RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "symbolic-ref", "HEAD", BranchPrefix+name).RunInDir(repo.Path)
return err return err
} }
// GetDefaultBranch gets default branch of repository. // GetDefaultBranch gets default branch of repository.
func (repo *Repository) GetDefaultBranch() (string, error) { func (repo *Repository) GetDefaultBranch() (string, error) {
return NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path) return NewCommandContext(repo.Ctx, "symbolic-ref", "HEAD").RunInDir(repo.Path)
} }
// GetBranch returns a branch by it's name // GetBranch returns a branch by it's name
@ -118,7 +119,7 @@ type DeleteBranchOptions struct {
// DeleteBranch delete a branch by name on repository. // DeleteBranch delete a branch by name on repository.
func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error { func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error {
cmd := NewCommand("branch") cmd := NewCommandContext(repo.Ctx, "branch")
if opts.Force { if opts.Force {
cmd.AddArguments("-D") cmd.AddArguments("-D")
@ -134,7 +135,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
// CreateBranch create a new branch // CreateBranch create a new branch
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
cmd := NewCommand("branch") cmd := NewCommandContext(repo.Ctx, "branch")
cmd.AddArguments("--", branch, oldbranchOrCommit) cmd.AddArguments("--", branch, oldbranchOrCommit)
_, err := cmd.RunInDir(repo.Path) _, err := cmd.RunInDir(repo.Path)
@ -144,7 +145,7 @@ func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
// AddRemote adds a new remote to repository. // AddRemote adds a new remote to repository.
func (repo *Repository) AddRemote(name, url string, fetch bool) error { func (repo *Repository) AddRemote(name, url string, fetch bool) error {
cmd := NewCommand("remote", "add") cmd := NewCommandContext(repo.Ctx, "remote", "add")
if fetch { if fetch {
cmd.AddArguments("-f") cmd.AddArguments("-f")
} }
@ -156,7 +157,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
// RemoveRemote removes a remote from repository. // RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error { func (repo *Repository) RemoveRemote(name string) error {
_, err := NewCommand("remote", "rm", name).RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "remote", "rm", name).RunInDir(repo.Path)
return err return err
} }
@ -167,6 +168,6 @@ func (branch *Branch) GetCommit() (*Commit, error) {
// RenameBranch rename a branch // RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error { func (repo *Repository) RenameBranch(from, to string) error {
_, err := NewCommand("branch", "-m", from, to).RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "branch", "-m", from, to).RunInDir(repo.Path)
return err return err
} }

View File

@ -11,6 +11,7 @@ package git
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"io" "io"
"strings" "strings"
@ -23,7 +24,7 @@ func (repo *Repository) IsObjectExist(name string) bool {
return false return false
} }
wr, rd, cancel := repo.CatFileBatchCheck() wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel() defer cancel()
_, err := wr.Write([]byte(name + "\n")) _, err := wr.Write([]byte(name + "\n"))
if err != nil { if err != nil {
@ -40,7 +41,7 @@ func (repo *Repository) IsReferenceExist(name string) bool {
return false return false
} }
wr, rd, cancel := repo.CatFileBatchCheck() wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel() defer cancel()
_, err := wr.Write([]byte(name + "\n")) _, err := wr.Write([]byte(name + "\n"))
if err != nil { if err != nil {
@ -63,11 +64,11 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranches returns branches from the repository, skipping skip initial branches and // GetBranches returns branches from the repository, skipping skip initial branches and
// returning at most limit branches, or all branches if limit is 0. // returning at most limit branches, or all branches if limit is 0.
func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) { func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) {
return callShowRef(repo.Path, BranchPrefix, "--heads", skip, limit) return callShowRef(repo.Ctx, repo.Path, BranchPrefix, "--heads", skip, limit)
} }
// callShowRef return refs, if limit = 0 it will not limit // callShowRef return refs, if limit = 0 it will not limit
func callShowRef(repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) { func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe() stdoutReader, stdoutWriter := io.Pipe()
defer func() { defer func() {
_ = stdoutReader.Close() _ = stdoutReader.Close()
@ -76,7 +77,7 @@ func callShowRef(repoPath, prefix, arg string, skip, limit int) (branchNames []s
go func() { go func() {
stderrBuilder := &strings.Builder{} stderrBuilder := &strings.Builder{}
err := NewCommand("show-ref", arg).RunInDirPipeline(repoPath, stdoutWriter, stderrBuilder) err := NewCommandContext(ctx, "show-ref", arg).RunInDirPipeline(repoPath, stdoutWriter, stderrBuilder)
if err != nil { if err != nil {
if stderrBuilder.Len() == 0 { if stderrBuilder.Len() == 0 {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()

View File

@ -58,7 +58,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
relpath = `\` + relpath relpath = `\` + relpath
} }
stdout, err := NewCommand("log", "-1", prettyLogFormat, id.String(), "--", relpath).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "log", "-1", prettyLogFormat, id.String(), "--", relpath).RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -73,7 +73,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
// GetCommitByPath returns the last commit of relative path. // GetCommitByPath returns the last commit of relative path.
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
stdout, err := NewCommand("log", "-1", prettyLogFormat, "--", relpath).RunInDirBytes(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "log", "-1", prettyLogFormat, "--", relpath).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -86,7 +86,7 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
} }
func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit, error) { func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit, error) {
stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize), stdout, err := NewCommandContext(repo.Ctx, "log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize),
"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path) "--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
@ -97,7 +97,7 @@ func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit,
func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Commit, error) { func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Commit, error) {
// create new git log command with limit of 100 commis // create new git log command with limit of 100 commis
cmd := NewCommand("log", id.String(), "-100", prettyLogFormat) cmd := NewCommandContext(repo.Ctx, "log", id.String(), "-100", prettyLogFormat)
// ignore case // ignore case
args := []string{"-i"} args := []string{"-i"}
@ -155,7 +155,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
// ignore anything below 4 characters as too unspecific // ignore anything below 4 characters as too unspecific
if len(v) >= 4 { if len(v) >= 4 {
// create new git log command with 1 commit limit // create new git log command with 1 commit limit
hashCmd := NewCommand("log", "-1", prettyLogFormat) hashCmd := NewCommandContext(repo.Ctx, "log", "-1", prettyLogFormat)
// add previous arguments except for --grep and --all // add previous arguments except for --grep and --all
hashCmd.AddArguments(args...) hashCmd.AddArguments(args...)
// add keyword as <commit> // add keyword as <commit>
@ -176,7 +176,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
} }
func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) { func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
stdout, err := NewCommand("diff", "--name-only", id1, id2).RunInDirBytes(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "diff", "--name-only", id1, id2).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -186,7 +186,7 @@ func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
// FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2 // FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2
// You must ensure that id1 and id2 are valid commit ids. // You must ensure that id1 and id2 are valid commit ids.
func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) { func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) {
stdout, err := NewCommand("diff", "--name-only", "-z", id1, id2, "--", filename).RunInDirBytes(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "diff", "--name-only", "-z", id1, id2, "--", filename).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -209,7 +209,7 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
}() }()
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := NewCommand("log", revision, "--follow", err := NewCommandContext(repo.Ctx, "log", revision, "--follow",
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page), "--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page),
prettyLogFormat, "--", file). prettyLogFormat, "--", file).
RunInDirPipeline(repo.Path, stdoutWriter, &stderr) RunInDirPipeline(repo.Path, stdoutWriter, &stderr)
@ -240,7 +240,7 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
// CommitsByFileAndRangeNoFollow return the commits according revision file and the page // CommitsByFileAndRangeNoFollow return the commits according revision file and the page
func (repo *Repository) CommitsByFileAndRangeNoFollow(revision, file string, page int) ([]*Commit, error) { func (repo *Repository) CommitsByFileAndRangeNoFollow(revision, file string, page int) ([]*Commit, error) {
stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*50), stdout, err := NewCommandContext(repo.Ctx, "log", revision, "--skip="+strconv.Itoa((page-1)*50),
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize), prettyLogFormat, "--", file).RunInDirBytes(repo.Path) "--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize), prettyLogFormat, "--", file).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
@ -250,11 +250,11 @@ func (repo *Repository) CommitsByFileAndRangeNoFollow(revision, file string, pag
// FilesCountBetween return the number of files changed between two commits // FilesCountBetween return the number of files changed between two commits
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) { func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
stdout, err := NewCommand("diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated. // git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated.
// previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that... // previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that...
stdout, err = NewCommand("diff", "--name-only", startCommitID, endCommitID).RunInDir(repo.Path) stdout, err = NewCommandContext(repo.Ctx, "diff", "--name-only", startCommitID, endCommitID).RunInDir(repo.Path)
} }
if err != nil { if err != nil {
return 0, err return 0, err
@ -268,13 +268,13 @@ func (repo *Repository) CommitsBetween(last *Commit, before *Commit) ([]*Commit,
var stdout []byte var stdout []byte
var err error var err error
if before == nil { if before == nil {
stdout, err = NewCommand("rev-list", last.ID.String()).RunInDirBytes(repo.Path) stdout, err = NewCommandContext(repo.Ctx, "rev-list", last.ID.String()).RunInDirBytes(repo.Path)
} else { } else {
stdout, err = NewCommand("rev-list", before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path) stdout, err = NewCommandContext(repo.Ctx, "rev-list", before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that... // previously it would return the results of git rev-list before last so let's try that...
stdout, err = NewCommand("rev-list", before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path) stdout, err = NewCommandContext(repo.Ctx, "rev-list", before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
} }
} }
if err != nil { if err != nil {
@ -288,13 +288,13 @@ func (repo *Repository) CommitsBetweenLimit(last *Commit, before *Commit, limit,
var stdout []byte var stdout []byte
var err error var err error
if before == nil { if before == nil {
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunInDirBytes(repo.Path) stdout, err = NewCommandContext(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunInDirBytes(repo.Path)
} else { } else {
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path) stdout, err = NewCommandContext(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+".."+last.ID.String()).RunInDirBytes(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list --max-count n before last so let's try that... // previously it would return the results of git rev-list --max-count n before last so let's try that...
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path) stdout, err = NewCommandContext(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
} }
} }
if err != nil { if err != nil {
@ -333,7 +333,7 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
// commitsBefore the limit is depth, not total number of returned commits. // commitsBefore the limit is depth, not total number of returned commits.
func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) { func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) {
cmd := NewCommand("log") cmd := NewCommandContext(repo.Ctx, "log")
if limit > 0 { if limit > 0 {
cmd.AddArguments("-"+strconv.Itoa(limit), prettyLogFormat, id.String()) cmd.AddArguments("-"+strconv.Itoa(limit), prettyLogFormat, id.String())
} else { } else {
@ -377,7 +377,7 @@ func (repo *Repository) getCommitsBeforeLimit(id SHA1, num int) ([]*Commit, erro
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) { func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
if CheckGitVersionAtLeast("2.7.0") == nil { if CheckGitVersionAtLeast("2.7.0") == nil {
stdout, err := NewCommand("for-each-ref", "--count="+strconv.Itoa(limit), "--format=%(refname:strip=2)", "--contains", commit.ID.String(), BranchPrefix).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "for-each-ref", "--count="+strconv.Itoa(limit), "--format=%(refname:strip=2)", "--contains", commit.ID.String(), BranchPrefix).RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -386,7 +386,7 @@ func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error)
return branches, nil return branches, nil
} }
stdout, err := NewCommand("branch", "--contains", commit.ID.String()).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "branch", "--contains", commit.ID.String()).RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -425,7 +425,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit {
// IsCommitInBranch check if the commit is on the branch // IsCommitInBranch check if the commit is on the branch
func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) { func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) {
stdout, err := NewCommand("branch", "--contains", commitID, branch).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "branch", "--contains", commitID, branch).RunInDir(repo.Path)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -40,7 +40,7 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
} }
} }
actualCommitID, err := NewCommand("rev-parse", "--verify", commitID).RunInDir(repo.Path) actualCommitID, err := NewCommandContext(repo.Ctx, "rev-parse", "--verify", commitID).RunInDir(repo.Path)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") || if strings.Contains(err.Error(), "unknown revision or path") ||
strings.Contains(err.Error(), "fatal: Needed a single revision") { strings.Contains(err.Error(), "fatal: Needed a single revision") {

View File

@ -18,7 +18,7 @@ import (
// ResolveReference resolves a name to a reference // ResolveReference resolves a name to a reference
func (repo *Repository) ResolveReference(name string) (string, error) { func (repo *Repository) ResolveReference(name string) (string, error) {
stdout, err := NewCommand("show-ref", "--hash", name).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "show-ref", "--hash", name).RunInDir(repo.Path)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "not a valid ref") { if strings.Contains(err.Error(), "not a valid ref") {
return "", ErrNotExist{name, ""} return "", ErrNotExist{name, ""}
@ -35,7 +35,7 @@ func (repo *Repository) ResolveReference(name string) (string, error) {
// GetRefCommitID returns the last commit ID string of given reference (branch or tag). // GetRefCommitID returns the last commit ID string of given reference (branch or tag).
func (repo *Repository) GetRefCommitID(name string) (string, error) { func (repo *Repository) GetRefCommitID(name string) (string, error) {
wr, rd, cancel := repo.CatFileBatchCheck() wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel() defer cancel()
_, _ = wr.Write([]byte(name + "\n")) _, _ = wr.Write([]byte(name + "\n"))
shaBs, _, _, err := ReadBatchLine(rd) shaBs, _, _, err := ReadBatchLine(rd)
@ -48,12 +48,12 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
// IsCommitExist returns true if given commit exists in current repository. // IsCommitExist returns true if given commit exists in current repository.
func (repo *Repository) IsCommitExist(name string) bool { func (repo *Repository) IsCommitExist(name string) bool {
_, err := NewCommand("cat-file", "-e", name).RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "cat-file", "-e", name).RunInDir(repo.Path)
return err == nil return err == nil
} }
func (repo *Repository) getCommit(id SHA1) (*Commit, error) { func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
wr, rd, cancel := repo.CatFileBatch() wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel() defer cancel()
_, _ = wr.Write([]byte(id.String() + "\n")) _, _ = wr.Write([]byte(id.String() + "\n"))
@ -132,7 +132,7 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
} }
} }
wr, rd, cancel := repo.CatFileBatchCheck() wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel() defer cancel()
_, err := wr.Write([]byte(commitID + "\n")) _, err := wr.Write([]byte(commitID + "\n"))
if err != nil { if err != nil {

View File

@ -35,13 +35,13 @@ func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (strin
if tmpRemote != "origin" { if tmpRemote != "origin" {
tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base
// Fetch commit into a temporary branch in order to be able to handle commits and tags // Fetch commit into a temporary branch in order to be able to handle commits and tags
_, err := NewCommand("fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path)
if err == nil { if err == nil {
base = tmpBaseName base = tmpBaseName
} }
} }
stdout, err := NewCommand("merge-base", "--", base, head).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "merge-base", "--", base, head).RunInDir(repo.Path)
return strings.TrimSpace(stdout), base, err return strings.TrimSpace(stdout), base, err
} }
@ -88,7 +88,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string,
// We have a common base - therefore we know that ... should work // We have a common base - therefore we know that ... should work
if !fileOnly { if !fileOnly {
logs, err := NewCommand("log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path) logs, err := NewCommandContext(repo.Ctx, "log", baseCommitID+separator+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -141,14 +141,14 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
separator = ".." separator = ".."
} }
if err := NewCommand("diff", "-z", "--name-only", base+separator+head). if err := NewCommandContext(repo.Ctx, "diff", "-z", "--name-only", base+separator+head).
RunInDirPipeline(repo.Path, w, stderr); err != nil { RunInDirPipeline(repo.Path, w, stderr); err != nil {
if strings.Contains(stderr.String(), "no merge base") { if strings.Contains(stderr.String(), "no merge base") {
// git >= 2.28 now returns an error if base and head have become unrelated. // git >= 2.28 now returns an error if base and head have become unrelated.
// previously it would return the results of git diff -z --name-only base head so let's try that... // previously it would return the results of git diff -z --name-only base head so let's try that...
w = &lineCountWriter{} w = &lineCountWriter{}
stderr.Reset() stderr.Reset()
if err = NewCommand("diff", "-z", "--name-only", base, head).RunInDirPipeline(repo.Path, w, stderr); err == nil { if err = NewCommandContext(repo.Ctx, "diff", "-z", "--name-only", base, head).RunInDirPipeline(repo.Path, w, stderr); err == nil {
return w.numLines, nil return w.numLines, nil
} }
} }
@ -231,23 +231,23 @@ func (repo *Repository) GetDiffOrPatch(base, head string, w io.Writer, patch, bi
// GetDiff generates and returns patch data between given revisions, optimized for human readability // GetDiff generates and returns patch data between given revisions, optimized for human readability
func (repo *Repository) GetDiff(base, head string, w io.Writer) error { func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
return NewCommand("diff", "-p", base, head). return NewCommandContext(repo.Ctx, "diff", "-p", base, head).
RunInDirPipeline(repo.Path, w, nil) RunInDirPipeline(repo.Path, w, nil)
} }
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs. // GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error { func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
return NewCommand("diff", "-p", "--binary", base, head). return NewCommandContext(repo.Ctx, "diff", "-p", "--binary", base, head).
RunInDirPipeline(repo.Path, w, nil) RunInDirPipeline(repo.Path, w, nil)
} }
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply` // GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
func (repo *Repository) GetPatch(base, head string, w io.Writer) error { func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := NewCommand("format-patch", "--binary", "--stdout", base+"..."+head). err := NewCommandContext(repo.Ctx, "format-patch", "--binary", "--stdout", base+"..."+head).
RunInDirPipeline(repo.Path, w, stderr) RunInDirPipeline(repo.Path, w, stderr)
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) { if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return NewCommand("format-patch", "--binary", "--stdout", base, head). return NewCommandContext(repo.Ctx, "format-patch", "--binary", "--stdout", base, head).
RunInDirPipeline(repo.Path, w, nil) RunInDirPipeline(repo.Path, w, nil)
} }
return err return err
@ -256,7 +256,7 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
// GetDiffFromMergeBase generates and return patch data from merge base to head // GetDiffFromMergeBase generates and return patch data from merge base to head
func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error { func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := NewCommand("diff", "-p", "--binary", base+"..."+head). err := NewCommandContext(repo.Ctx, "diff", "-p", "--binary", base+"..."+head).
RunInDirPipeline(repo.Path, w, stderr) RunInDirPipeline(repo.Path, w, stderr)
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) { if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return repo.GetDiffBinary(base, head, w) return repo.GetDiffBinary(base, head, w)

View File

@ -34,7 +34,7 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
Sign: true, Sign: true,
} }
value, _ := NewCommand("config", "--get", "commit.gpgsign").RunInDir(repo.Path) value, _ := NewCommandContext(repo.Ctx, "config", "--get", "commit.gpgsign").RunInDir(repo.Path)
sign, valid := ParseBool(strings.TrimSpace(value)) sign, valid := ParseBool(strings.TrimSpace(value))
if !sign || !valid { if !sign || !valid {
gpgSettings.Sign = false gpgSettings.Sign = false
@ -42,13 +42,13 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
return gpgSettings, nil return gpgSettings, nil
} }
signingKey, _ := NewCommand("config", "--get", "user.signingkey").RunInDir(repo.Path) signingKey, _ := NewCommandContext(repo.Ctx, "config", "--get", "user.signingkey").RunInDir(repo.Path)
gpgSettings.KeyID = strings.TrimSpace(signingKey) gpgSettings.KeyID = strings.TrimSpace(signingKey)
defaultEmail, _ := NewCommand("config", "--get", "user.email").RunInDir(repo.Path) defaultEmail, _ := NewCommandContext(repo.Ctx, "config", "--get", "user.email").RunInDir(repo.Path)
gpgSettings.Email = strings.TrimSpace(defaultEmail) gpgSettings.Email = strings.TrimSpace(defaultEmail)
defaultName, _ := NewCommand("config", "--get", "user.name").RunInDir(repo.Path) defaultName, _ := NewCommandContext(repo.Ctx, "config", "--get", "user.name").RunInDir(repo.Path)
gpgSettings.Name = strings.TrimSpace(defaultName) gpgSettings.Name = strings.TrimSpace(defaultName)
if err := gpgSettings.LoadPublicKeyContent(); err != nil { if err := gpgSettings.LoadPublicKeyContent(); err != nil {

View File

@ -18,7 +18,7 @@ import (
// ReadTreeToIndex reads a treeish to the index // ReadTreeToIndex reads a treeish to the index
func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error { func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
if len(treeish) != 40 { if len(treeish) != 40 {
res, err := NewCommand("rev-parse", "--verify", treeish).RunInDir(repo.Path) res, err := NewCommandContext(repo.Ctx, "rev-parse", "--verify", treeish).RunInDir(repo.Path)
if err != nil { if err != nil {
return err return err
} }
@ -38,7 +38,7 @@ func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error
if len(indexFilename) > 0 { if len(indexFilename) > 0 {
env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0]) env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
} }
_, err := NewCommand("read-tree", id.String()).RunInDirWithEnv(repo.Path, env) _, err := NewCommandContext(repo.Ctx, "read-tree", id.String()).RunInDirWithEnv(repo.Path, env)
if err != nil { if err != nil {
return err return err
} }
@ -69,13 +69,13 @@ func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (filename, tmpD
// EmptyIndex empties the index // EmptyIndex empties the index
func (repo *Repository) EmptyIndex() error { func (repo *Repository) EmptyIndex() error {
_, err := NewCommand("read-tree", "--empty").RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "read-tree", "--empty").RunInDir(repo.Path)
return err return err
} }
// LsFiles checks if the given filenames are in the index // LsFiles checks if the given filenames are in the index
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
cmd := NewCommand("ls-files", "-z", "--") cmd := NewCommandContext(repo.Ctx, "ls-files", "-z", "--")
for _, arg := range filenames { for _, arg := range filenames {
if arg != "" { if arg != "" {
cmd.AddArguments(arg) cmd.AddArguments(arg)
@ -95,7 +95,7 @@ func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
// RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present.
func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
cmd := NewCommand("update-index", "--remove", "-z", "--index-info") cmd := NewCommandContext(repo.Ctx, "update-index", "--remove", "-z", "--index-info")
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
@ -111,14 +111,14 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
// AddObjectToIndex adds the provided object hash to the index at the provided filename // AddObjectToIndex adds the provided object hash to the index at the provided filename
func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error { func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error {
cmd := NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename) cmd := NewCommandContext(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename)
_, err := cmd.RunInDir(repo.Path) _, err := cmd.RunInDir(repo.Path)
return err return err
} }
// WriteTree writes the current index as a tree to the object db and returns its hash // WriteTree writes the current index as a tree to the object db and returns its hash
func (repo *Repository) WriteTree() (*Tree, error) { func (repo *Repository) WriteTree() (*Tree, error) {
res, err := NewCommand("write-tree").RunInDir(repo.Path) res, err := NewCommandContext(repo.Ctx, "write-tree").RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -25,7 +25,7 @@ import (
func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, error) { func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, error) {
// We will feed the commit IDs in order into cat-file --batch, followed by blobs as necessary. // We will feed the commit IDs in order into cat-file --batch, followed by blobs as necessary.
// so let's create a batch stdin and stdout // so let's create a batch stdin and stdout
batchStdinWriter, batchReader, cancel := repo.CatFileBatch() batchStdinWriter, batchReader, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel() defer cancel()
writeID := func(id string) error { writeID := func(id string) error {
@ -76,7 +76,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
IndexFile: indexFilename, IndexFile: indexFilename,
WorkTree: worktree, WorkTree: worktree,
} }
ctx, cancel := context.WithCancel(DefaultContext) ctx, cancel := context.WithCancel(repo.Ctx)
if err := checker.Init(ctx); err != nil { if err := checker.Init(ctx); err != nil {
log.Error("Unable to open checker for %s. Error: %v", commitID, err) log.Error("Unable to open checker for %s. Error: %v", commitID, err)
} else { } else {
@ -96,6 +96,12 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
var content []byte var content []byte
sizes := make(map[string]int64) sizes := make(map[string]int64)
for _, f := range entries { for _, f := range entries {
select {
case <-repo.Ctx.Done():
return sizes, repo.Ctx.Err()
default:
}
contentBuf.Reset() contentBuf.Reset()
content = contentBuf.Bytes() content = contentBuf.Bytes()

View File

@ -42,7 +42,7 @@ func (repo *Repository) HashObject(reader io.Reader) (SHA1, error) {
} }
func (repo *Repository) hashObject(reader io.Reader) (string, error) { func (repo *Repository) hashObject(reader io.Reader) (string, error) {
cmd := NewCommand("hash-object", "-w", "--stdin") cmd := NewCommandContext(repo.Ctx, "hash-object", "-w", "--stdin")
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, reader) err := cmd.RunInDirFullPipeline(repo.Path, stdout, stderr, reader)

View File

@ -23,7 +23,7 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
go func() { go func() {
stderrBuilder := &strings.Builder{} stderrBuilder := &strings.Builder{}
err := NewCommand("for-each-ref").RunInDirPipeline(repo.Path, stdoutWriter, stderrBuilder) err := NewCommandContext(repo.Ctx, "for-each-ref").RunInDirPipeline(repo.Path, stdoutWriter, stderrBuilder)
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String())) _ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String()))
} else { } else {

View File

@ -39,7 +39,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
since := fromTime.Format(time.RFC3339) since := fromTime.Format(time.RFC3339)
stdout, err := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunInDirBytes(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -67,7 +67,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
} }
stderr := new(strings.Builder) stderr := new(strings.Builder)
err = NewCommand(args...).RunInDirTimeoutEnvFullPipelineFunc( err = NewCommandContext(repo.Ctx, args...).RunInDirTimeoutEnvFullPipelineFunc(
nil, -1, repo.Path, nil, -1, repo.Path,
stdoutWriter, stderr, nil, stdoutWriter, stderr, nil,
func(ctx context.Context, cancel context.CancelFunc) error { func(ctx context.Context, cancel context.CancelFunc) error {

View File

@ -6,6 +6,7 @@
package git package git
import ( import (
"context"
"fmt" "fmt"
"strings" "strings"
@ -17,19 +18,19 @@ import (
const TagPrefix = "refs/tags/" const TagPrefix = "refs/tags/"
// IsTagExist returns true if given tag exists in the repository. // IsTagExist returns true if given tag exists in the repository.
func IsTagExist(repoPath, name string) bool { func IsTagExist(ctx context.Context, repoPath, name string) bool {
return IsReferenceExist(repoPath, TagPrefix+name) return IsReferenceExist(ctx, repoPath, TagPrefix+name)
} }
// CreateTag create one tag in the repository // CreateTag create one tag in the repository
func (repo *Repository) CreateTag(name, revision string) error { func (repo *Repository) CreateTag(name, revision string) error {
_, err := NewCommand("tag", "--", name, revision).RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "tag", "--", name, revision).RunInDir(repo.Path)
return err return err
} }
// CreateAnnotatedTag create one annotated tag in the repository // CreateAnnotatedTag create one annotated tag in the repository
func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error { func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error {
_, err := NewCommand("tag", "-a", "-m", message, "--", name, revision).RunInDir(repo.Path) _, err := NewCommandContext(repo.Ctx, "tag", "-a", "-m", message, "--", name, revision).RunInDir(repo.Path)
return err return err
} }
@ -79,7 +80,7 @@ func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
} }
// The tag is an annotated tag with a message. // The tag is an annotated tag with a message.
data, err := NewCommand("cat-file", "-p", tagID.String()).RunInDirBytes(repo.Path) data, err := NewCommandContext(repo.Ctx, "cat-file", "-p", tagID.String()).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -104,7 +105,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
return "", fmt.Errorf("SHA is too short: %s", sha) return "", fmt.Errorf("SHA is too short: %s", sha)
} }
stdout, err := NewCommand("show-ref", "--tags", "-d").RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "show-ref", "--tags", "-d").RunInDir(repo.Path)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -127,7 +128,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
// GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA) // GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA)
func (repo *Repository) GetTagID(name string) (string, error) { func (repo *Repository) GetTagID(name string) (string, error) {
stdout, err := NewCommand("show-ref", "--tags", "--", name).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "show-ref", "--tags", "--", name).RunInDir(repo.Path)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -163,7 +164,7 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
// GetTagInfos returns all tag infos of the repository. // GetTagInfos returns all tag infos of the repository.
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
// TODO this a slow implementation, makes one git command per tag // TODO this a slow implementation, makes one git command per tag
stdout, err := NewCommand("tag").RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "tag").RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -196,7 +197,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated) // GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
func (repo *Repository) GetTagType(id SHA1) (string, error) { func (repo *Repository) GetTagType(id SHA1) (string, error) {
// Get tag type // Get tag type
stdout, err := NewCommand("cat-file", "-t", id.String()).RunInDir(repo.Path) stdout, err := NewCommandContext(repo.Ctx, "cat-file", "-t", id.String()).RunInDir(repo.Path)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -20,6 +20,6 @@ func (repo *Repository) IsTagExist(name string) bool {
// GetTags returns all tags of the repository. // GetTags returns all tags of the repository.
// returning at most limit tags, or all if limit is 0. // returning at most limit tags, or all if limit is 0.
func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) { func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
tags, _, err = callShowRef(repo.Path, TagPrefix, "--tags", skip, limit) tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
return return
} }

View File

@ -40,7 +40,7 @@ func (repo *Repository) CommitTree(author *Signature, committer *Signature, tree
"GIT_COMMITTER_EMAIL="+committer.Email, "GIT_COMMITTER_EMAIL="+committer.Email,
"GIT_COMMITTER_DATE="+commitTimeStr, "GIT_COMMITTER_DATE="+commitTimeStr,
) )
cmd := NewCommand("commit-tree", tree.ID.String()) cmd := NewCommandContext(repo.Ctx, "commit-tree", tree.ID.String())
for _, parent := range opts.Parents { for _, parent := range opts.Parents {
cmd.AddArguments("-p", parent) cmd.AddArguments("-p", parent)

View File

@ -22,7 +22,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
// GetTree find the tree object in the repository. // GetTree find the tree object in the repository.
func (repo *Repository) GetTree(idStr string) (*Tree, error) { func (repo *Repository) GetTree(idStr string) (*Tree, error) {
if len(idStr) != 40 { if len(idStr) != 40 {
res, err := NewCommand("rev-parse", "--verify", idStr).RunInDir(repo.Path) res, err := NewCommandContext(repo.Ctx, "rev-parse", "--verify", idStr).RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -12,7 +12,7 @@ import (
) )
func (repo *Repository) getTree(id SHA1) (*Tree, error) { func (repo *Repository) getTree(id SHA1) (*Tree, error) {
wr, rd, cancel := repo.CatFileBatch() wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel() defer cancel()
_, _ = wr.Write([]byte(id.String() + "\n")) _, _ = wr.Write([]byte(id.String() + "\n"))

View File

@ -36,7 +36,7 @@ func (t *Tree) ListEntries() (Entries, error) {
} }
if t.repo != nil { if t.repo != nil {
wr, rd, cancel := t.repo.CatFileBatch() wr, rd, cancel := t.repo.CatFileBatch(t.repo.Ctx)
defer cancel() defer cancel()
_, _ = wr.Write([]byte(t.ID.String() + "\n")) _, _ = wr.Write([]byte(t.ID.String() + "\n"))

View File

@ -275,7 +275,7 @@ func (b *BleveIndexer) Index(repo *models.Repository, sha string, changes *repoC
batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize) batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
if len(changes.Updates) > 0 { if len(changes.Updates) > 0 {
batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath()) batchWriter, batchReader, cancel := git.CatFileBatch(git.DefaultContext, repo.RepoPath())
defer cancel() defer cancel()
for _, update := range changes.Updates { for _, update := range changes.Updates {

View File

@ -248,7 +248,7 @@ func (b *ElasticSearchIndexer) Index(repo *models.Repository, sha string, change
reqs := make([]elastic.BulkableRequest, 0) reqs := make([]elastic.BulkableRequest, 0)
if len(changes.Updates) > 0 { if len(changes.Updates) > 0 {
batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath()) batchWriter, batchReader, cancel := git.CatFileBatch(git.DefaultContext, repo.RepoPath())
defer cancel() defer cancel()
for _, update := range changes.Updates { for _, update := range changes.Updates {

View File

@ -5,9 +5,13 @@
package stats package stats
import ( import (
"fmt"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
) )
// DBIndexer implements Indexer interface to use database's like search // DBIndexer implements Indexer interface to use database's like search
@ -16,6 +20,9 @@ type DBIndexer struct {
// Index repository status function // Index repository status function
func (db *DBIndexer) Index(id int64) error { func (db *DBIndexer) Index(id int64) error {
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().ShutdownContext(), fmt.Sprintf("Stats.DB Index Repo[%d]", id))
defer finished()
repo, err := models.GetRepositoryByID(id) repo, err := models.GetRepositoryByID(id)
if err != nil { if err != nil {
return err return err
@ -29,7 +36,7 @@ func (db *DBIndexer) Index(id int64) error {
return err return err
} }
gitRepo, err := git.OpenRepository(repo.RepoPath()) gitRepo, err := git.OpenRepositoryCtx(ctx, repo.RepoPath())
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,7 +5,6 @@
package external package external
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -107,11 +106,8 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
ctx.Ctx = graceful.GetManager().ShutdownContext() ctx.Ctx = graceful.GetManager().ShutdownContext()
} }
processCtx, cancel := context.WithCancel(ctx.Ctx) processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.URLPrefix))
defer cancel() defer finished()
pid := process.GetManager().Add(fmt.Sprintf("Render [%s] for %s", commands[0], ctx.URLPrefix), cancel)
defer process.GetManager().Remove(pid)
cmd := exec.CommandContext(processCtx, commands[0], args...) cmd := exec.CommandContext(processCtx, commands[0], args...)
cmd.Env = append( cmd.Env = append(

View File

@ -0,0 +1,69 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package process
import (
"context"
)
// Context is a wrapper around context.Context and contains the current pid for this context
type Context struct {
context.Context
pid IDType
}
// GetPID returns the PID for this context
func (c *Context) GetPID() IDType {
return c.pid
}
// GetParent returns the parent process context (if any)
func (c *Context) GetParent() *Context {
return GetContext(c.Context)
}
// Value is part of the interface for context.Context. We mostly defer to the internal context - but we return this in response to the ProcessContextKey
func (c *Context) Value(key interface{}) interface{} {
if key == ProcessContextKey {
return c
}
return c.Context.Value(key)
}
// ProcessContextKey is the key under which process contexts are stored
var ProcessContextKey interface{} = "process-context"
// GetContext will return a process context if one exists
func GetContext(ctx context.Context) *Context {
if pCtx, ok := ctx.(*Context); ok {
return pCtx
}
pCtxInterface := ctx.Value(ProcessContextKey)
if pCtxInterface == nil {
return nil
}
if pCtx, ok := pCtxInterface.(*Context); ok {
return pCtx
}
return nil
}
// GetPID returns the PID for this context
func GetPID(ctx context.Context) IDType {
pCtx := GetContext(ctx)
if pCtx == nil {
return ""
}
return pCtx.GetPID()
}
// GetParentPID returns the ParentPID for this context
func GetParentPID(ctx context.Context) IDType {
var parentPID IDType
if parentProcess := GetContext(ctx); parentProcess != nil {
parentPID = parentProcess.GetPID()
}
return parentPID
}

View File

@ -12,6 +12,7 @@ import (
"io" "io"
"os/exec" "os/exec"
"sort" "sort"
"strconv"
"sync" "sync"
"time" "time"
) )
@ -28,57 +29,151 @@ var (
DefaultContext = context.Background() DefaultContext = context.Background()
) )
// Process represents a working process inheriting from Gitea. // IDType is a pid type
type Process struct { type IDType string
PID int64 // Process ID, not system one.
Description string
Start time.Time
Cancel context.CancelFunc
}
// Manager knows about all processes and counts PIDs. // FinishedFunc is a function that marks that the process is finished and can be removed from the process table
// - it is simply an alias for context.CancelFunc and is only for documentary purposes
type FinishedFunc = context.CancelFunc
// Manager manages all processes and counts PIDs.
type Manager struct { type Manager struct {
mutex sync.Mutex mutex sync.Mutex
counter int64 next int64
processes map[int64]*Process lastTime int64
processes map[IDType]*Process
} }
// GetManager returns a Manager and initializes one as singleton if there's none yet // GetManager returns a Manager and initializes one as singleton if there's none yet
func GetManager() *Manager { func GetManager() *Manager {
managerInit.Do(func() { managerInit.Do(func() {
manager = &Manager{ manager = &Manager{
processes: make(map[int64]*Process), processes: make(map[IDType]*Process),
next: 1,
} }
}) })
return manager return manager
} }
// Add a process to the ProcessManager and returns its PID. // AddContext creates a new context and adds it as a process. Once the process is finished, finished must be called
func (pm *Manager) Add(description string, cancel context.CancelFunc) int64 { // to remove the process from the process table. It should not be called until the process is finished but must always be called.
//
// cancel should be used to cancel the returned context, however it will not remove the process from the process table.
// finished will cancel the returned context and remove it from the process table.
//
// Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the
// process table.
func (pm *Manager) AddContext(parent context.Context, description string) (ctx context.Context, cancel context.CancelFunc, finished FinishedFunc) {
parentPID := GetParentPID(parent)
ctx, cancel = context.WithCancel(parent)
pid, finished := pm.Add(parentPID, description, cancel)
return &Context{
Context: ctx,
pid: pid,
}, cancel, finished
}
// AddContextTimeout creates a new context and add it as a process. Once the process is finished, finished must be called
// to remove the process from the process table. It should not be called until the process is finsihed but must always be called.
//
// cancel should be used to cancel the returned context, however it will not remove the process from the process table.
// finished will cancel the returned context and remove it from the process table.
//
// Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the
// process table.
func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) {
parentPID := GetParentPID(parent)
ctx, cancel = context.WithTimeout(parent, timeout)
pid, finshed := pm.Add(parentPID, description, cancel)
return &Context{
Context: ctx,
pid: pid,
}, cancel, finshed
}
// Add create a new process
func (pm *Manager) Add(parentPID IDType, description string, cancel context.CancelFunc) (IDType, FinishedFunc) {
pm.mutex.Lock() pm.mutex.Lock()
pid := pm.counter + 1 start, pid := pm.nextPID()
pm.processes[pid] = &Process{
parent := pm.processes[parentPID]
if parent == nil {
parentPID = ""
}
process := &Process{
PID: pid, PID: pid,
ParentPID: parentPID,
Description: description, Description: description,
Start: time.Now(), Start: start,
Cancel: cancel, Cancel: cancel,
} }
pm.counter = pid
finished := func() {
cancel()
pm.remove(process)
}
if parent != nil {
parent.AddChild(process)
}
pm.processes[pid] = process
pm.mutex.Unlock() pm.mutex.Unlock()
return pid return pid, finished
}
// nextPID will return the next available PID. pm.mutex should already be locked.
func (pm *Manager) nextPID() (start time.Time, pid IDType) {
start = time.Now()
startUnix := start.Unix()
if pm.lastTime == startUnix {
pm.next++
} else {
pm.next = 1
}
pm.lastTime = startUnix
pid = IDType(strconv.FormatInt(start.Unix(), 16))
if pm.next == 1 {
return
}
pid = IDType(string(pid) + "-" + strconv.FormatInt(pm.next, 10))
return
} }
// Remove a process from the ProcessManager. // Remove a process from the ProcessManager.
func (pm *Manager) Remove(pid int64) { func (pm *Manager) Remove(pid IDType) {
pm.mutex.Lock() pm.mutex.Lock()
delete(pm.processes, pid) delete(pm.processes, pid)
pm.mutex.Unlock() pm.mutex.Unlock()
} }
func (pm *Manager) remove(process *Process) {
pm.mutex.Lock()
if p := pm.processes[process.PID]; p == process {
delete(pm.processes, process.PID)
}
parent := pm.processes[process.ParentPID]
pm.mutex.Unlock()
if parent == nil {
return
}
parent.RemoveChild(process)
}
// Cancel a process in the ProcessManager. // Cancel a process in the ProcessManager.
func (pm *Manager) Cancel(pid int64) { func (pm *Manager) Cancel(pid IDType) {
pm.mutex.Lock() pm.mutex.Lock()
process, ok := pm.processes[pid] process, ok := pm.processes[pid]
pm.mutex.Unlock() pm.mutex.Unlock()
@ -88,14 +183,28 @@ func (pm *Manager) Cancel(pid int64) {
} }
// Processes gets the processes in a thread safe manner // Processes gets the processes in a thread safe manner
func (pm *Manager) Processes() []*Process { func (pm *Manager) Processes(onlyRoots bool) []*Process {
pm.mutex.Lock() pm.mutex.Lock()
processes := make([]*Process, 0, len(pm.processes)) processes := make([]*Process, 0, len(pm.processes))
for _, process := range pm.processes { if onlyRoots {
processes = append(processes, process) for _, process := range pm.processes {
if _, has := pm.processes[process.ParentPID]; !has {
processes = append(processes, process)
}
}
} else {
for _, process := range pm.processes {
processes = append(processes, process)
}
} }
pm.mutex.Unlock() pm.mutex.Unlock()
sort.Sort(processList(processes))
sort.Slice(processes, func(i, j int) bool {
left, right := processes[i], processes[j]
return left.Start.Before(right.Start)
})
return processes return processes
} }
@ -134,8 +243,8 @@ func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env
stdOut := new(bytes.Buffer) stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
ctx, cancel := context.WithTimeout(DefaultContext, timeout) ctx, _, finished := pm.AddContextTimeout(DefaultContext, timeout, desc)
defer cancel() defer finished()
cmd := exec.CommandContext(ctx, cmdName, args...) cmd := exec.CommandContext(ctx, cmdName, args...)
cmd.Dir = dir cmd.Dir = dir
@ -150,13 +259,11 @@ func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env
return "", "", err return "", "", err
} }
pid := pm.Add(desc, cancel)
err := cmd.Wait() err := cmd.Wait()
pm.Remove(pid)
if err != nil { if err != nil {
err = &Error{ err = &Error{
PID: pid, PID: GetPID(ctx),
Description: desc, Description: desc,
Err: err, Err: err,
CtxErr: ctx.Err(), CtxErr: ctx.Err(),
@ -168,23 +275,9 @@ func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env
return stdOut.String(), stdErr.String(), err return stdOut.String(), stdErr.String(), err
} }
type processList []*Process
func (l processList) Len() int {
return len(l)
}
func (l processList) Less(i, j int) bool {
return l[i].PID < l[j].PID
}
func (l processList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
// Error is a wrapped error describing the error results of Process Execution // Error is a wrapped error describing the error results of Process Execution
type Error struct { type Error struct {
PID int64 PID IDType
Description string Description string
Err error Err error
CtxErr error CtxErr error
@ -193,7 +286,7 @@ type Error struct {
} }
func (err *Error) Error() string { func (err *Error) Error() string {
return fmt.Sprintf("exec(%d:%s) failed: %v(%v) stdout: %s stderr: %s", err.PID, err.Description, err.Err, err.CtxErr, err.Stdout, err.Stderr) return fmt.Sprintf("exec(%s:%s) failed: %v(%v) stdout: %s stderr: %s", err.PID, err.Description, err.Err, err.CtxErr, err.Stdout, err.Stderr)
} }
// Unwrap implements the unwrappable implicit interface for go1.13 Unwrap() // Unwrap implements the unwrappable implicit interface for go1.13 Unwrap()

View File

@ -21,44 +21,72 @@ func TestGetManager(t *testing.T) {
assert.NotNil(t, pm) assert.NotNil(t, pm)
} }
func TestManager_Add(t *testing.T) { func TestManager_AddContext(t *testing.T) {
pm := Manager{processes: make(map[int64]*Process)} pm := Manager{processes: make(map[IDType]*Process), next: 1}
pid := pm.Add("foo", nil) ctx, cancel := context.WithCancel(context.Background())
assert.Equal(t, int64(1), pid, "expected to get pid 1 got %d", pid) defer cancel()
pid = pm.Add("bar", nil) p1Ctx, _, finished := pm.AddContext(ctx, "foo")
assert.Equal(t, int64(2), pid, "expected to get pid 2 got %d", pid) defer finished()
assert.NotEmpty(t, GetContext(p1Ctx).GetPID(), "expected to get non-empty pid")
p2Ctx, _, finished := pm.AddContext(p1Ctx, "bar")
defer finished()
assert.NotEmpty(t, GetContext(p2Ctx).GetPID(), "expected to get non-empty pid")
assert.NotEqual(t, GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetPID(), "expected to get different pids %s == %s", GetContext(p2Ctx).GetPID(), GetContext(p1Ctx).GetPID())
assert.Equal(t, GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetParent().GetPID(), "expected to get pid %s got %s", GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetParent().GetPID())
} }
func TestManager_Cancel(t *testing.T) { func TestManager_Cancel(t *testing.T) {
pm := Manager{processes: make(map[int64]*Process)} pm := Manager{processes: make(map[IDType]*Process), next: 1}
ctx, cancel := context.WithCancel(context.Background()) ctx, _, finished := pm.AddContext(context.Background(), "foo")
pid := pm.Add("foo", cancel) defer finished()
pm.Cancel(pid) pm.Cancel(GetPID(ctx))
select { select {
case <-ctx.Done(): case <-ctx.Done():
default: default:
assert.Fail(t, "Cancel should cancel the provided context") assert.Fail(t, "Cancel should cancel the provided context")
} }
finished()
ctx, cancel, finished := pm.AddContext(context.Background(), "foo")
defer finished()
cancel()
select {
case <-ctx.Done():
default:
assert.Fail(t, "Cancel should cancel the provided context")
}
finished()
} }
func TestManager_Remove(t *testing.T) { func TestManager_Remove(t *testing.T) {
pm := Manager{processes: make(map[int64]*Process)} pm := Manager{processes: make(map[IDType]*Process), next: 1}
pid1 := pm.Add("foo", nil) ctx, cancel := context.WithCancel(context.Background())
assert.Equal(t, int64(1), pid1, "expected to get pid 1 got %d", pid1) defer cancel()
pid2 := pm.Add("bar", nil) p1Ctx, _, finished := pm.AddContext(ctx, "foo")
assert.Equal(t, int64(2), pid2, "expected to get pid 2 got %d", pid2) defer finished()
assert.NotEmpty(t, GetContext(p1Ctx).GetPID(), "expected to have non-empty PID")
pm.Remove(pid2) p2Ctx, _, finished := pm.AddContext(p1Ctx, "bar")
defer finished()
_, exists := pm.processes[pid2] assert.NotEqual(t, GetContext(p1Ctx).GetPID(), GetContext(p2Ctx).GetPID(), "expected to get different pids got %s == %s", GetContext(p2Ctx).GetPID(), GetContext(p1Ctx).GetPID())
assert.False(t, exists, "PID %d is in the list but shouldn't", pid2)
pm.Remove(GetPID(p2Ctx))
_, exists := pm.processes[GetPID(p2Ctx)]
assert.False(t, exists, "PID %d is in the list but shouldn't", GetPID(p2Ctx))
} }
func TestExecTimeoutNever(t *testing.T) { func TestExecTimeoutNever(t *testing.T) {

View File

@ -0,0 +1,66 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package process
import (
"context"
"sync"
"time"
)
// Process represents a working process inheriting from Gitea.
type Process struct {
PID IDType // Process ID, not system one.
ParentPID IDType
Description string
Start time.Time
Cancel context.CancelFunc
lock sync.Mutex
children []*Process
}
// Children gets the children of the process
// Note: this function will behave nicely even if p is nil
func (p *Process) Children() (children []*Process) {
if p == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
children = make([]*Process, len(p.children))
copy(children, p.children)
return children
}
// AddChild adds a child process
// Note: this function will behave nicely even if p is nil
func (p *Process) AddChild(child *Process) {
if p == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
p.children = append(p.children, child)
}
// RemoveChild removes a child process
// Note: this function will behave nicely even if p is nil
func (p *Process) RemoveChild(process *Process) {
if p == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
for i, child := range p.children {
if child == process {
p.children = append(p.children[:i], p.children[i+1:]...)
return
}
}
}

View File

@ -957,7 +957,7 @@ type remoteAddress struct {
func mirrorRemoteAddress(m models.RemoteMirrorer) remoteAddress { func mirrorRemoteAddress(m models.RemoteMirrorer) remoteAddress {
a := remoteAddress{} a := remoteAddress{}
u, err := git.GetRemoteAddress(m.GetRepository().RepoPath(), m.GetRemoteName()) u, err := git.GetRemoteAddress(git.DefaultContext, m.GetRepository().RepoPath(), m.GetRemoteName())
if err != nil { if err != nil {
log.Error("GetRemoteAddress %v", err) log.Error("GetRemoteAddress %v", err)
return a return a

View File

@ -2696,6 +2696,7 @@ monitor.execute_time = Execution Time
monitor.process.cancel = Cancel process monitor.process.cancel = Cancel process
monitor.process.cancel_desc = Cancelling a process may cause data loss monitor.process.cancel_desc = Cancelling a process may cause data loss
monitor.process.cancel_notices = Cancel: <strong>%s</strong>? monitor.process.cancel_notices = Cancel: <strong>%s</strong>?
monitor.process.children = Children
monitor.queues = Queues monitor.queues = Queues
monitor.queue = Queue: %s monitor.queue = Queue: %s
monitor.queue.name = Name monitor.queue.name = Name

View File

@ -405,7 +405,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
repo := ctx.Repo.Repository repo := ctx.Repo.Repository
// Currently protection must match an actual branch // Currently protection must match an actual branch
if !git.IsBranchExist(ctx.Repo.Repository.RepoPath(), form.BranchName) { if !git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), form.BranchName) {
ctx.NotFound() ctx.NotFound()
return return
} }

View File

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/chi-middleware/proxy" "github.com/chi-middleware/proxy"
@ -22,7 +23,9 @@ func Middlewares() []func(http.Handler) http.Handler {
var handlers = []func(http.Handler) http.Handler{ var handlers = []func(http.Handler) http.Handler{
func(next http.Handler) http.Handler { func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
next.ServeHTTP(context.NewResponse(resp), req) ctx, _, finished := process.GetManager().AddContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI))
defer finished()
next.ServeHTTP(context.NewResponse(resp), req.WithContext(ctx))
}) })
}, },
} }

View File

@ -326,7 +326,7 @@ func Monitor(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.monitor") ctx.Data["Title"] = ctx.Tr("admin.monitor")
ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminMonitor"] = true ctx.Data["PageIsAdminMonitor"] = true
ctx.Data["Processes"] = process.GetManager().Processes() ctx.Data["Processes"] = process.GetManager().Processes(true)
ctx.Data["Entries"] = cron.ListTasks() ctx.Data["Entries"] = cron.ListTasks()
ctx.Data["Queues"] = queue.GetManager().ManagedQueues() ctx.Data["Queues"] = queue.GetManager().ManagedQueues()
ctx.HTML(http.StatusOK, tplMonitor) ctx.HTML(http.StatusOK, tplMonitor)
@ -334,8 +334,8 @@ func Monitor(ctx *context.Context) {
// MonitorCancel cancels a process // MonitorCancel cancels a process
func MonitorCancel(ctx *context.Context) { func MonitorCancel(ctx *context.Context) {
pid := ctx.ParamsInt64("pid") pid := ctx.Params("pid")
process.GetManager().Cancel(pid) process.GetManager().Cancel(process.IDType(pid))
ctx.JSON(http.StatusOK, map[string]interface{}{ ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": setting.AppSubURL + "/admin/monitor", "redirect": setting.AppSubURL + "/admin/monitor",
}) })

View File

@ -124,7 +124,7 @@ func RestoreBranchPost(ctx *context.Context) {
return return
} }
if err := git.Push(ctx.Repo.Repository.RepoPath(), git.PushOptions{ if err := git.Push(ctx, ctx.Repo.Repository.RepoPath(), git.PushOptions{
Remote: ctx.Repo.Repository.RepoPath(), Remote: ctx.Repo.Repository.RepoPath(),
Branch: fmt.Sprintf("%s:%s%s", deletedBranch.Commit, git.BranchPrefix, deletedBranch.Name), Branch: fmt.Sprintf("%s:%s%s", deletedBranch.Commit, git.BranchPrefix, deletedBranch.Name),
Env: models.PushingEnvironment(ctx.User, ctx.Repo.Repository), Env: models.PushingEnvironment(ctx.User, ctx.Repo.Repository),

View File

@ -8,7 +8,6 @@ package repo
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
gocontext "context"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
@ -485,8 +484,10 @@ func serviceRPC(h serviceHandler, service string) {
h.environ = append(h.environ, "GIT_PROTOCOL="+protocol) h.environ = append(h.environ, "GIT_PROTOCOL="+protocol)
} }
ctx, cancel := gocontext.WithCancel(git.DefaultContext) // ctx, cancel := gocontext.WithCancel(git.DefaultContext)
defer cancel() ctx, _, finished := process.GetManager().AddContext(h.r.Context(), fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.dir))
defer finished()
var stderr bytes.Buffer var stderr bytes.Buffer
cmd := exec.CommandContext(ctx, git.GitExecutable, service, "--stateless-rpc", h.dir) cmd := exec.CommandContext(ctx, git.GitExecutable, service, "--stateless-rpc", h.dir)
cmd.Dir = h.dir cmd.Dir = h.dir
@ -495,9 +496,6 @@ func serviceRPC(h serviceHandler, service string) {
cmd.Stdin = reqBody cmd.Stdin = reqBody
cmd.Stderr = &stderr cmd.Stderr = &stderr
pid := process.GetManager().Add(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.dir), cancel)
defer process.GetManager().Remove(pid)
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.dir, err, stderr.String()) log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.dir, err, stderr.String())
return return

View File

@ -1594,7 +1594,7 @@ func ViewIssue(ctx *context.Context) {
} }
ctx.Data["IsPullBranchDeletable"] = canDelete && ctx.Data["IsPullBranchDeletable"] = canDelete &&
pull.HeadRepo != nil && pull.HeadRepo != nil &&
git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch) && git.IsBranchExist(ctx, pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
(!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"]) (!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"])
stillCanManualMerge := func() bool { stillCanManualMerge := func() bool {

View File

@ -436,7 +436,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
if pull.Flow == models.PullRequestFlowGithub { if pull.Flow == models.PullRequestFlowGithub {
headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch) headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
} else { } else {
headBranchExist = git.IsReferenceExist(baseGitRepo.Path, pull.GetGitRefName()) headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName())
} }
if headBranchExist { if headBranchExist {

View File

@ -178,7 +178,7 @@ func SettingsPost(ctx *context.Context) {
} }
} }
u, _ := git.GetRemoteAddress(ctx.Repo.Repository.RepoPath(), ctx.Repo.Mirror.GetRemoteName()) u, _ := git.GetRemoteAddress(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Mirror.GetRemoteName())
if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() { if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() {
form.MirrorPassword, _ = u.User.Password() form.MirrorPassword, _ = u.User.Password()
} }

View File

@ -82,11 +82,10 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
} }
}() }()
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) { graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
ctx, cancel := context.WithCancel(baseCtx)
defer cancel()
pm := process.GetManager() pm := process.GetManager()
pid := pm.Add(config.FormatMessage(t.Name, "process", doer), cancel) ctx, _, finished := pm.AddContext(baseCtx, config.FormatMessage(t.Name, "process", doer))
defer pm.Remove(pid) defer finished()
if err := t.fun(ctx, doer, config); err != nil { if err := t.fun(ctx, doer, config); err != nil {
if db.IsErrCancelled(err) { if db.IsErrCancelled(err) {
message := err.(db.ErrCancelled).Message message := err.(db.ErrCancelled).Message

View File

@ -1303,8 +1303,9 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
return nil, err return nil, err
} }
ctx, cancel := context.WithTimeout(git.DefaultContext, time.Duration(setting.Git.Timeout.Default)*time.Second) timeout := time.Duration(setting.Git.Timeout.Default) * time.Second
defer cancel() ctx, _, finished := process.GetManager().AddContextTimeout(gitRepo.Ctx, timeout, fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath))
defer finished()
argsLength := 6 argsLength := 6
if len(opts.WhitespaceBehavior) > 0 { if len(opts.WhitespaceBehavior) > 0 {
@ -1369,9 +1370,6 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
return nil, fmt.Errorf("error during Start: %w", err) return nil, fmt.Errorf("error during Start: %w", err)
} }
pid := process.GetManager().Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cancel)
defer process.GetManager().Remove(pid)
diff, err := ParsePatch(opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, stdout, parsePatchSkipToFile) diff, err := ParsePatch(opts.MaxLines, opts.MaxLineCharacters, opts.MaxFiles, stdout, parsePatchSkipToFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to ParsePatch: %w", err) return nil, fmt.Errorf("unable to ParsePatch: %w", err)

View File

@ -7,7 +7,6 @@ package mailer
import ( import (
"bytes" "bytes"
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
@ -258,11 +257,10 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
args = append(args, to...) args = append(args, to...)
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args) log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
pm := process.GetManager()
desc := fmt.Sprintf("SendMail: %s %v", setting.MailService.SendmailPath, args) desc := fmt.Sprintf("SendMail: %s %v", setting.MailService.SendmailPath, args)
ctx, cancel := context.WithTimeout(graceful.GetManager().HammerContext(), setting.MailService.SendmailTimeout) ctx, _, finished := process.GetManager().AddContextTimeout(graceful.GetManager().HammerContext(), setting.MailService.SendmailTimeout, desc)
defer cancel() defer finished()
cmd := exec.CommandContext(ctx, setting.MailService.SendmailPath, args...) cmd := exec.CommandContext(ctx, setting.MailService.SendmailPath, args...)
pipe, err := cmd.StdinPipe() pipe, err := cmd.StdinPipe()
@ -272,18 +270,17 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
} }
if err = cmd.Start(); err != nil { if err = cmd.Start(); err != nil {
_ = pipe.Close()
return err return err
} }
pid := pm.Add(desc, cancel)
_, err = msg.WriteTo(pipe) _, err = msg.WriteTo(pipe)
// we MUST close the pipe or sendmail will hang waiting for more of the message // we MUST close the pipe or sendmail will hang waiting for more of the message
// Also we should wait on our sendmail command even if something fails // Also we should wait on our sendmail command even if something fails
closeError = pipe.Close() closeError = pipe.Close()
waitError = cmd.Wait() waitError = cmd.Wait()
pm.Remove(pid)
if err != nil { if err != nil {
return err return err
} else if closeError != nil { } else if closeError != nil {

View File

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/process"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
@ -192,7 +193,7 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
} }
gitArgs = append(gitArgs, m.GetRemoteName()) gitArgs = append(gitArgs, m.GetRemoteName())
remoteAddr, remoteErr := git.GetRemoteAddress(repoPath, m.GetRemoteName()) remoteAddr, remoteErr := git.GetRemoteAddress(ctx, repoPath, m.GetRemoteName())
if remoteErr != nil { if remoteErr != nil {
log.Error("GetRemoteAddress Error %v", remoteErr) log.Error("GetRemoteAddress Error %v", remoteErr)
} }
@ -287,7 +288,7 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool)
// sanitize the output, since it may contain the remote address, which may // sanitize the output, since it may contain the remote address, which may
// contain a password // contain a password
remoteAddr, remoteErr := git.GetRemoteAddress(wikiPath, m.GetRemoteName()) remoteAddr, remoteErr := git.GetRemoteAddress(ctx, wikiPath, m.GetRemoteName())
if remoteErr != nil { if remoteErr != nil {
log.Error("GetRemoteAddress Error %v", remoteErr) log.Error("GetRemoteAddress Error %v", remoteErr)
} }
@ -367,6 +368,9 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
return false return false
} }
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Syncing Mirror %s/%s", m.Repo.OwnerName, m.Repo.Name))
defer finished()
log.Trace("SyncMirrors [repo: %-v]: Running Sync", m.Repo) log.Trace("SyncMirrors [repo: %-v]: Running Sync", m.Repo)
results, ok := runSync(ctx, m) results, ok := runSync(ctx, m)
if !ok { if !ok {
@ -385,7 +389,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo) log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo)
} else { } else {
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
gitRepo, err = git.OpenRepository(m.Repo.RepoPath()) gitRepo, err = git.OpenRepositoryCtx(ctx, m.Repo.RepoPath())
if err != nil { if err != nil {
log.Error("OpenRepository [%d]: %v", m.RepoID, err) log.Error("OpenRepository [%d]: %v", m.RepoID, err)
return false return false

View File

@ -7,6 +7,7 @@ package mirror
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"regexp" "regexp"
"time" "time"
@ -15,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
@ -92,6 +94,9 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
m.LastError = "" m.LastError = ""
ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Syncing PushMirror %s/%s to %s", m.Repo.OwnerName, m.Repo.Name, m.RemoteName))
defer finished()
log.Trace("SyncPushMirror [mirror: %d][repo: %-v]: Running Sync", m.ID, m.Repo) log.Trace("SyncPushMirror [mirror: %d][repo: %-v]: Running Sync", m.ID, m.Repo)
err = runPushSync(ctx, m) err = runPushSync(ctx, m)
if err != nil { if err != nil {
@ -116,7 +121,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second
performPush := func(path string) error { performPush := func(path string) error {
remoteAddr, err := git.GetRemoteAddress(path, m.RemoteName) remoteAddr, err := git.GetRemoteAddress(ctx, path, m.RemoteName)
if err != nil { if err != nil {
log.Error("GetRemoteAddress(%s) Error %v", path, err) log.Error("GetRemoteAddress(%s) Error %v", path, err)
return errors.New("Unexpected error") return errors.New("Unexpected error")
@ -125,7 +130,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
if setting.LFS.StartServer { if setting.LFS.StartServer {
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
gitRepo, err := git.OpenRepository(path) gitRepo, err := git.OpenRepositoryCtx(ctx, path)
if err != nil { if err != nil {
log.Error("OpenRepository: %v", err) log.Error("OpenRepository: %v", err)
return errors.New("Unexpected error") return errors.New("Unexpected error")
@ -141,7 +146,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
log.Trace("Pushing %s mirror[%d] remote %s", path, m.ID, m.RemoteName) log.Trace("Pushing %s mirror[%d] remote %s", path, m.ID, m.RemoteName)
if err := git.Push(path, git.PushOptions{ if err := git.Push(ctx, path, git.PushOptions{
Remote: m.RemoteName, Remote: m.RemoteName,
Force: true, Force: true,
Mirror: true, Mirror: true,
@ -162,7 +167,7 @@ func runPushSync(ctx context.Context, m *models.PushMirror) error {
if m.Repo.HasWiki() { if m.Repo.HasWiki() {
wikiPath := m.Repo.WikiPath() wikiPath := m.Repo.WikiPath()
_, err := git.GetRemoteAddress(wikiPath, m.RemoteName) _, err := git.GetRemoteAddress(ctx, wikiPath, m.RemoteName)
if err == nil { if err == nil {
err := performPush(wikiPath) err := performPush(wikiPath)
if err != nil { if err != nil {

View File

@ -112,7 +112,7 @@ func GetPullRequestCommitStatusState(pr *models.PullRequest) (structs.CommitStat
if pr.Flow == models.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) { if pr.Flow == models.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) {
return "", errors.New("Head branch does not exist, can not merge") return "", errors.New("Head branch does not exist, can not merge")
} }
if pr.Flow == models.PullRequestFlowAGit && !git.IsReferenceExist(headGitRepo.Path, pr.GetGitRefName()) { if pr.Flow == models.PullRequestFlowAGit && !git.IsReferenceExist(headGitRepo.Ctx, headGitRepo.Path, pr.GetGitRefName()) {
return "", errors.New("Head branch does not exist, can not merge") return "", errors.New("Head branch does not exist, can not merge")
} }

View File

@ -313,7 +313,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
for _, pr := range prs { for _, pr := range prs {
divergence, err := GetDiverging(pr) divergence, err := GetDiverging(pr)
if err != nil { if err != nil {
if models.IsErrBranchDoesNotExist(err) && !git.IsBranchExist(pr.HeadRepo.RepoPath(), pr.HeadBranch) { if models.IsErrBranchDoesNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch)
} else { } else {
log.Error("GetDiverging: %v", err) log.Error("GetDiverging: %v", err)
@ -431,7 +431,7 @@ func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err
gitRefName := pr.GetGitRefName() gitRefName := pr.GetGitRefName()
if err := git.Push(headRepoPath, git.PushOptions{ if err := git.Push(git.DefaultContext, headRepoPath, git.PushOptions{
Remote: baseRepoPath, Remote: baseRepoPath,
Branch: prefixHeadBranch + pr.HeadBranch + ":" + gitRefName, Branch: prefixHeadBranch + pr.HeadBranch + ":" + gitRefName,
Force: true, Force: true,

View File

@ -152,7 +152,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) {
if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { if err := models.RemoveTemporaryPath(tmpBasePath); err != nil {
log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err)
} }
if !git.IsBranchExist(pr.HeadRepo.RepoPath(), pr.HeadBranch) { if !git.IsBranchExist(git.DefaultContext, pr.HeadRepo.RepoPath(), pr.HeadBranch) {
return "", models.ErrBranchDoesNotExist{ return "", models.ErrBranchDoesNotExist{
BranchName: pr.HeadBranch, BranchName: pr.HeadBranch,
} }

View File

@ -24,13 +24,13 @@ func CreateNewBranch(doer *user_model.User, repo *models.Repository, oldBranchNa
return err return err
} }
if !git.IsBranchExist(repo.RepoPath(), oldBranchName) { if !git.IsBranchExist(git.DefaultContext, repo.RepoPath(), oldBranchName) {
return models.ErrBranchDoesNotExist{ return models.ErrBranchDoesNotExist{
BranchName: oldBranchName, BranchName: oldBranchName,
} }
} }
if err := git.Push(repo.RepoPath(), git.PushOptions{ if err := git.Push(git.DefaultContext, repo.RepoPath(), git.PushOptions{
Remote: repo.RepoPath(), Remote: repo.RepoPath(),
Branch: fmt.Sprintf("%s:%s%s", oldBranchName, git.BranchPrefix, branchName), Branch: fmt.Sprintf("%s:%s%s", oldBranchName, git.BranchPrefix, branchName),
Env: models.PushingEnvironment(doer, repo), Env: models.PushingEnvironment(doer, repo),
@ -106,7 +106,7 @@ func CreateNewBranchFromCommit(doer *user_model.User, repo *models.Repository, c
return err return err
} }
if err := git.Push(repo.RepoPath(), git.PushOptions{ if err := git.Push(git.DefaultContext, repo.RepoPath(), git.PushOptions{
Remote: repo.RepoPath(), Remote: repo.RepoPath(),
Branch: fmt.Sprintf("%s:%s%s", commit, git.BranchPrefix, branchName), Branch: fmt.Sprintf("%s:%s%s", commit, git.BranchPrefix, branchName),
Env: models.PushingEnvironment(doer, repo), Env: models.PushingEnvironment(doer, repo),

View File

@ -264,7 +264,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *user_m
func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash string, branch string) error { func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash string, branch string) error {
// Because calls hooks we need to pass in the environment // Because calls hooks we need to pass in the environment
env := models.PushingEnvironment(doer, t.repo) env := models.PushingEnvironment(doer, t.repo)
if err := git.Push(t.basePath, git.PushOptions{ if err := git.Push(t.gitRepo.Ctx, t.basePath, git.PushOptions{
Remote: t.repo.RepoPath(), Remote: t.repo.RepoPath(),
Branch: strings.TrimSpace(commitHash) + ":refs/heads/" + strings.TrimSpace(branch), Branch: strings.TrimSpace(commitHash) + ":refs/heads/" + strings.TrimSpace(branch),
Env: env, Env: env,

View File

@ -5,7 +5,6 @@
package task package task
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -99,11 +98,9 @@ func runMigrateTask(t *models.Task) (err error) {
opts.MigrateToRepoID = t.RepoID opts.MigrateToRepoID = t.RepoID
ctx, cancel := context.WithCancel(graceful.GetManager().ShutdownContext())
defer cancel()
pm := process.GetManager() pm := process.GetManager()
pid := pm.Add(fmt.Sprintf("MigrateTask: %s/%s", t.Owner.Name, opts.RepoName), cancel) ctx, _, finished := pm.AddContext(graceful.GetManager().ShutdownContext(), fmt.Sprintf("MigrateTask: %s/%s", t.Owner.Name, opts.RepoName))
defer pm.Remove(pid) defer finished()
t.StartTime = timeutil.TimeStampNow() t.StartTime = timeutil.TimeStampNow()
t.Status = structs.TaskStatusRunning t.Status = structs.TaskStatusRunning

View File

@ -128,7 +128,7 @@ func updateWikiPage(doer *user_model.User, repo *models.Repository, oldWikiName,
return fmt.Errorf("InitWiki: %v", err) return fmt.Errorf("InitWiki: %v", err)
} }
hasMasterBranch := git.IsBranchExist(repo.WikiPath(), "master") hasMasterBranch := git.IsBranchExist(git.DefaultContext, repo.WikiPath(), "master")
basePath, err := models.CreateTemporaryPath("update-wiki") basePath, err := models.CreateTemporaryPath("update-wiki")
if err != nil { if err != nil {
@ -243,7 +243,7 @@ func updateWikiPage(doer *user_model.User, repo *models.Repository, oldWikiName,
return err return err
} }
if err := git.Push(basePath, git.PushOptions{ if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{
Remote: "origin", Remote: "origin",
Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"), Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
Env: models.FullPushingEnvironment( Env: models.FullPushingEnvironment(
@ -357,7 +357,7 @@ func DeleteWikiPage(doer *user_model.User, repo *models.Repository, wikiName str
return err return err
} }
if err := git.Push(basePath, git.PushOptions{ if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{
Remote: "origin", Remote: "origin",
Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"), Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
Env: models.PushingEnvironment(doer, repo), Env: models.PushingEnvironment(doer, repo),

View File

@ -65,33 +65,7 @@
</table> </table>
</div> </div>
<h4 class="ui top attached header"> {{template "admin/process" .}}
{{.i18n.Tr "admin.monitor.process"}}
</h4>
<div class="ui attached table segment">
<table class="ui very basic striped table">
<thead>
<tr>
<th>Pid</th>
<th>{{.i18n.Tr "admin.monitor.desc"}}</th>
<th>{{.i18n.Tr "admin.monitor.start"}}</th>
<th>{{.i18n.Tr "admin.monitor.execute_time"}}</th>
<th></th>
</tr>
</thead>
<tbody>
{{range .Processes}}
<tr>
<td>{{.PID}}</td>
<td>{{.Description}}</td>
<td>{{DateFmtLong .Start}}</td>
<td>{{TimeSince .Start $.Lang}}</td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/cancel/{{.PID}}" data-id="{{.PID}}" data-name="{{.Description}}">{{svg "octicon-trash" 16 "text-red"}}</a></td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div> </div>
</div> </div>
<div class="ui small basic delete modal"> <div class="ui small basic delete modal">

View File

@ -0,0 +1,20 @@
<div class="item">
<div class="df ac">
<div class="content f1">
<div class="header">{{.Process.Description}}</div>
<div class="description"><span title="{{DateFmtLong .Process.Start}}">{{TimeSince .Process.Start .root.Lang}}</span></div>
</div>
<div>
<a class="delete-button icon" href="" data-url="{{.root.Link}}/cancel/{{.Process.PID}}" data-id="{{.Process.PID}}" data-name="{{.Process.Description}}">{{svg "octicon-trash" 16 "text-red"}}</a>
</div>
</div>
{{$children := .Process.Children}}
{{if $children}}
<div class="divided list">
{{range $children}}
{{template "admin/process-row" dict "Process" . "root" $.root}}
{{end}}
</div>
{{end}}
</div>

View File

@ -0,0 +1,10 @@
<h4 class="ui top attached header">
{{.i18n.Tr "admin.monitor.process"}}
</h4>
<div class="ui attached segment">
<div class="ui relaxed divided list">
{{range .Processes}}
{{template "admin/process-row" dict "Process" . "root" $}}
{{end}}
</div>
</div>