bovarys.me/fudge@v0.4.0/git/git.go (about) 1 package git 2 3 import ( 4 "io" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "sort" 9 "strings" 10 11 "github.com/dustin/go-humanize" 12 "gopkg.in/src-d/go-git.v4" 13 "gopkg.in/src-d/go-git.v4/plumbing/object" 14 ) 15 16 type Blob struct { 17 Name string 18 IsBinary bool 19 Size string // The blob humanized size 20 Reader io.ReadCloser 21 } 22 23 type TreeObject struct { 24 Name string 25 IsFile bool 26 Size string // The object humanized size 27 } 28 29 func isNotCandidate(path string) bool { 30 file, err := os.Stat(path) 31 isRegularFile := !os.IsNotExist(err) && !file.IsDir() 32 33 return isRegularFile || os.IsNotExist(err) 34 } 35 36 // OpenRepository opens a Git repository from the given root path and dirname. 37 // If strict is set to false, OpenRepository will try to open dirname first, 38 // then dirname with a ".git" suffix. 39 func OpenRepository(root, dirname string, strict bool) (*git.Repository, error) { 40 path := filepath.Join(root, dirname) 41 42 if isNotCandidate(path) { 43 if strict { 44 return nil, git.ErrRepositoryNotExists 45 } 46 47 path = filepath.Join(root, dirname+".git") 48 if isNotCandidate(path) { 49 return nil, git.ErrRepositoryNotExists 50 } 51 } 52 53 repository, err := git.PlainOpen(path) 54 55 return repository, err 56 } 57 58 func GetRepositoryNames(root string) ([]string, error) { 59 files, err := ioutil.ReadDir(root) 60 if err != nil { 61 return nil, err 62 } 63 64 var names []string 65 66 for _, file := range files { 67 if !file.IsDir() { 68 continue 69 } 70 71 _, err := OpenRepository(root, file.Name(), true) 72 if err == git.ErrRepositoryNotExists { 73 continue 74 } 75 if err != nil { 76 return nil, err 77 } 78 79 name := strings.TrimSuffix(file.Name(), ".git") 80 81 names = append(names, name) 82 } 83 84 return names, nil 85 } 86 87 func GetRepositoryCommits(r *git.Repository) ([]*object.Commit, error) { 88 iter, err := r.Log(&git.LogOptions{}) 89 if err != nil { 90 return nil, err 91 } 92 93 var commits []*object.Commit 94 95 err = iter.ForEach(func(c *object.Commit) error { 96 // XXX 97 c.Message = strings.Split(c.Message, "\n")[0] 98 commits = append(commits, c) 99 100 return nil 101 }) 102 if err != nil { 103 return nil, err 104 } 105 106 return commits, nil 107 } 108 109 func GetRepositoryLastCommit(r *git.Repository) (*object.Commit, error) { 110 head, err := r.Head() 111 if err != nil { 112 return nil, err 113 } 114 115 commit, err := r.CommitObject(head.Hash()) 116 if err != nil { 117 return nil, err 118 } 119 120 // XXX 121 commit.Message = strings.Split(commit.Message, "\n")[0] 122 123 return commit, nil 124 } 125 126 func GetRepositoryTree(r *git.Repository, path string) (*object.Tree, error) { 127 head, err := r.Head() 128 if err != nil { 129 return nil, err 130 } 131 132 commit, err := r.CommitObject(head.Hash()) 133 if err != nil { 134 return nil, err 135 } 136 137 tree, err := commit.Tree() 138 if err != nil { 139 return nil, err 140 } 141 142 if path != "" { 143 tree, err = tree.Tree(path) 144 if err != nil { 145 return nil, err 146 } 147 } 148 149 return tree, nil 150 } 151 152 func GetRepositoryBlob(r *git.Repository, path string) (*Blob, error) { 153 dir := filepath.Dir(path) 154 if dir == "." { 155 dir = "" 156 } 157 158 tree, err := GetRepositoryTree(r, dir) 159 if err != nil { 160 return nil, err 161 } 162 163 filename := filepath.Base(path) 164 file, err := tree.File(filename) 165 if err != nil { 166 return nil, err 167 } 168 169 isBinary, err := file.IsBinary() 170 if err != nil { 171 return nil, err 172 } 173 174 reader, err := file.Blob.Reader() 175 if err != nil { 176 return nil, err 177 } 178 179 blob := &Blob{ 180 Name: file.Name, 181 IsBinary: isBinary, 182 Size: humanize.Bytes(uint64(file.Blob.Size)), 183 Reader: reader, 184 } 185 186 return blob, nil 187 } 188 189 func GetTreeObjects(tree *object.Tree) ([]*TreeObject, error) { 190 var objects []*TreeObject 191 192 walker := object.NewTreeWalker(tree, false, nil) 193 194 for { 195 name, entry, err := walker.Next() 196 if err == io.EOF { 197 break 198 } 199 if err != nil { 200 return nil, err 201 } 202 203 size, err := tree.Size(name) 204 if err != nil { 205 return nil, err 206 } 207 208 o := &TreeObject{ 209 Name: name, 210 IsFile: entry.Mode.IsFile(), 211 Size: humanize.Bytes(uint64(size)), 212 } 213 214 objects = append(objects, o) 215 } 216 217 sort.Slice(objects, func(i, j int) bool { 218 // Order the objects by non-file status then name 219 if !objects[i].IsFile && objects[j].IsFile { 220 return true 221 } 222 223 if objects[i].IsFile && !objects[j].IsFile { 224 return false 225 } 226 227 return objects[i].Name < objects[j].Name 228 }) 229 230 return objects, nil 231 }