github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/repo_branch_nogogit.go (about)

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