code.gitea.io/gitea@v1.19.3/modules/git/repo_branch_nogogit.go (about)

     1  // Copyright 2015 The Gogs Authors. All rights reserved.
     2  // Copyright 2018 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  //go:build !gogit
     6  
     7  package git
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"context"
    13  	"io"
    14  	"strings"
    15  
    16  	"code.gitea.io/gitea/modules/log"
    17  )
    18  
    19  // IsObjectExist returns true if given reference exists in the repository.
    20  func (repo *Repository) IsObjectExist(name string) bool {
    21  	if name == "" {
    22  		return false
    23  	}
    24  
    25  	wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
    26  	defer cancel()
    27  	_, err := wr.Write([]byte(name + "\n"))
    28  	if err != nil {
    29  		log.Debug("Error writing to CatFileBatchCheck %v", err)
    30  		return false
    31  	}
    32  	sha, _, _, err := ReadBatchLine(rd)
    33  	return err == nil && bytes.HasPrefix(sha, []byte(strings.TrimSpace(name)))
    34  }
    35  
    36  // IsReferenceExist returns true if given reference exists in the repository.
    37  func (repo *Repository) IsReferenceExist(name string) bool {
    38  	if name == "" {
    39  		return false
    40  	}
    41  
    42  	wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
    43  	defer cancel()
    44  	_, err := wr.Write([]byte(name + "\n"))
    45  	if err != nil {
    46  		log.Debug("Error writing to CatFileBatchCheck %v", err)
    47  		return false
    48  	}
    49  	_, _, _, err = ReadBatchLine(rd)
    50  	return err == nil
    51  }
    52  
    53  // IsBranchExist returns true if given branch exists in current repository.
    54  func (repo *Repository) IsBranchExist(name string) bool {
    55  	if repo == nil || name == "" {
    56  		return false
    57  	}
    58  
    59  	return repo.IsReferenceExist(BranchPrefix + name)
    60  }
    61  
    62  // GetBranchNames returns branches from the repository, skipping "skip" initial branches and
    63  // returning at most "limit" branches, or all branches if "limit" is 0.
    64  func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
    65  	return callShowRef(repo.Ctx, repo.Path, BranchPrefix, TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit)
    66  }
    67  
    68  // WalkReferences walks all the references from the repository
    69  func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
    70  	return walkShowRef(ctx, repoPath, nil, 0, 0, walkfn)
    71  }
    72  
    73  // WalkReferences walks all the references from the repository
    74  // refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
    75  func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
    76  	var args TrustedCmdArgs
    77  	switch refType {
    78  	case ObjectTag:
    79  		args = TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"}
    80  	case ObjectBranch:
    81  		args = TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}
    82  	}
    83  
    84  	return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)
    85  }
    86  
    87  // callShowRef return refs, if limit = 0 it will not limit
    88  func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) {
    89  	countAll, err = walkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {
    90  		branchName = strings.TrimPrefix(branchName, trimPrefix)
    91  		branchNames = append(branchNames, branchName)
    92  
    93  		return nil
    94  	})
    95  	return branchNames, countAll, err
    96  }
    97  
    98  func walkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
    99  	stdoutReader, stdoutWriter := io.Pipe()
   100  	defer func() {
   101  		_ = stdoutReader.Close()
   102  		_ = stdoutWriter.Close()
   103  	}()
   104  
   105  	go func() {
   106  		stderrBuilder := &strings.Builder{}
   107  		args := TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
   108  		args = append(args, extraArgs...)
   109  		err := NewCommand(ctx, args...).Run(&RunOpts{
   110  			Dir:    repoPath,
   111  			Stdout: stdoutWriter,
   112  			Stderr: stderrBuilder,
   113  		})
   114  		if err != nil {
   115  			if stderrBuilder.Len() == 0 {
   116  				_ = stdoutWriter.Close()
   117  				return
   118  			}
   119  			_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String()))
   120  		} else {
   121  			_ = stdoutWriter.Close()
   122  		}
   123  	}()
   124  
   125  	i := 0
   126  	bufReader := bufio.NewReader(stdoutReader)
   127  	for i < skip {
   128  		_, isPrefix, err := bufReader.ReadLine()
   129  		if err == io.EOF {
   130  			return i, nil
   131  		}
   132  		if err != nil {
   133  			return 0, err
   134  		}
   135  		if !isPrefix {
   136  			i++
   137  		}
   138  	}
   139  	for limit == 0 || i < skip+limit {
   140  		// The output of show-ref is simply a list:
   141  		// <sha> SP <ref> LF
   142  		sha, err := bufReader.ReadString(' ')
   143  		if err == io.EOF {
   144  			return i, nil
   145  		}
   146  		if err != nil {
   147  			return 0, err
   148  		}
   149  
   150  		branchName, err := bufReader.ReadString('\n')
   151  		if err == io.EOF {
   152  			// This shouldn't happen... but we'll tolerate it for the sake of peace
   153  			return i, nil
   154  		}
   155  		if err != nil {
   156  			return i, err
   157  		}
   158  
   159  		if len(branchName) > 0 {
   160  			branchName = branchName[:len(branchName)-1]
   161  		}
   162  
   163  		if len(sha) > 0 {
   164  			sha = sha[:len(sha)-1]
   165  		}
   166  
   167  		err = walkfn(sha, branchName)
   168  		if err != nil {
   169  			return i, err
   170  		}
   171  		i++
   172  	}
   173  	// count all refs
   174  	for limit != 0 {
   175  		_, isPrefix, err := bufReader.ReadLine()
   176  		if err == io.EOF {
   177  			return i, nil
   178  		}
   179  		if err != nil {
   180  			return 0, err
   181  		}
   182  		if !isPrefix {
   183  			i++
   184  		}
   185  	}
   186  	return i, nil
   187  }
   188  
   189  // GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash
   190  func (repo *Repository) GetRefsBySha(sha, prefix string) ([]string, error) {
   191  	var revList []string
   192  	_, err := walkShowRef(repo.Ctx, repo.Path, nil, 0, 0, func(walkSha, refname string) error {
   193  		if walkSha == sha && strings.HasPrefix(refname, prefix) {
   194  			revList = append(revList, refname)
   195  		}
   196  		return nil
   197  	})
   198  	return revList, err
   199  }