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 }