mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Make Requests Processes and create process hierarchy. Associate OpenRepository with context. (#17125)
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:
		@@ -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)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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{}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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() {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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") {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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"))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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"))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								modules/markup/external/external.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								modules/markup/external/external.go
									
									
									
									
										vendored
									
									
								
							@@ -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(
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								modules/process/context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								modules/process/context.go
									
									
									
									
									
										Normal 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										66
									
								
								modules/process/process.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								modules/process/process.go
									
									
									
									
									
										Normal 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
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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))
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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">
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								templates/admin/process-row.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								templates/admin/process-row.tmpl
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
							
								
								
									
										10
									
								
								templates/admin/process.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								templates/admin/process.tmpl
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user