Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 package git | |
| 2 | |
| 3 import "bufio" | |
| 4 import "fmt" | |
| 5 import "os/exec" | |
| 6 import "strconv" | |
| 7 import "strings" | |
| 8 import "sync" | |
| 9 import "sync/atomic" | |
| 10 import "unsafe" | |
| 11 | |
| 12 import "infra/libs/infra_util" | |
| 13 | |
| 14 // GetObject will asynchronously fetch |objectish| from the Repo, and | |
| 15 // return a channel for the result. If the object is missing, GetObject will | |
| 16 // push nil to the channel. | |
| 17 func (r *Repo) GetObject(objectish string) InternableObject { | |
| 18 r.ensureCatFileServer(&r.catFile, false) | |
| 19 | |
| 20 rchan := make(chan *catFileReply, 1) | |
| 21 (*r.catFile) <- catFileRequest{ | |
| 22 objectish: objectish, | |
| 23 reply: rchan, | |
| 24 } | |
| 25 | |
| 26 if rsp := <-rchan; rsp == nil { | |
| 27 return nil | |
| 28 } else { | |
| 29 switch rsp.typ { | |
| 30 case "blob": | |
| 31 return BlobFromRawWithID(rsp.id, rsp.data) | |
| 32 case "tree": | |
| 33 if t, err := TreeFromRawWithID(rsp.id, rsp.data); err != nil { | |
| 34 panic(err) | |
| 35 } else { | |
| 36 return t | |
| 37 } | |
| 38 case "commit": | |
| 39 if c, err := CommitFromRawWithID(rsp.id, rsp.data); err != nil { | |
| 40 panic(err) | |
| 41 } else { | |
| 42 return c | |
| 43 } | |
| 44 default: | |
| 45 panic(fmt.Errorf("unsupported object type: %s", rsp.typ) ) | |
| 46 } | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 func (r *Repo) GetObjectID(id ObjectID) InternableObject { | |
| 51 return r.GetObject(id.String()) | |
| 52 } | |
| 53 | |
| 54 // HasObject will return true iff the Repo contains the objectish | |
| 55 func (r *Repo) HasObject(objectish string) bool { | |
| 56 r.ensureCatFileServer(&r.catFileCheck, true) | |
| 57 rchan := make(chan *catFileReply, 1) | |
| 58 (*r.catFileCheck) <- catFileRequest{ | |
| 59 objectish: objectish, | |
| 60 reply: rchan, | |
| 61 } | |
| 62 return (<-rchan) != nil | |
| 63 } | |
| 64 | |
| 65 func (r *Repo) HasObjectID(id ObjectID) bool { | |
| 66 return r.HasObject(id.String()) | |
| 67 } | |
| 68 | |
| 69 type blobOpt bool | |
| 70 | |
| 71 const ( | |
| 72 WithBlobs blobOpt = true | |
| 73 NoBlobs = false | |
| 74 ) | |
| 75 | |
| 76 type fullTree bool | |
| 77 | |
| 78 const ( | |
| 79 FullTree fullTree = true | |
|
M-A Ruel
2014/10/18 00:47:05
move constants and simple types at the top
iannucci
2014/10/20 21:11:57
Done.
| |
| 80 MissingOK = false | |
| 81 ) | |
| 82 | |
| 83 // GetFullTree gets a recursively-enumerated Tree. | |
| 84 // | |
| 85 // blobOpt: | |
| 86 // WithBlobs - Load blobs from Repo | |
| 87 // NoBlobs - Blobs will be EmptyObject | |
| 88 // | |
| 89 // fullTree: | |
| 90 // FullTree - All entries in tree must load | |
| 91 // MissingOK - Missing entries will remain EmptyObject (or an !Complete() Tree ) | |
| 92 func (r *Repo) GetFullTree(treeish string, b blobOpt, f fullTree) *Tree { | |
| 93 base, ok := r.GetObject(treeish).(*Tree) | |
| 94 if !ok { | |
| 95 if f == FullTree { | |
| 96 panic(fmt.Errorf("could not load object %s", treeish)) | |
| 97 } else { | |
| 98 return nil | |
| 99 } | |
| 100 } | |
| 101 grp := sync.WaitGroup{} | |
| 102 for p, c := range base.children { | |
| 103 p := p | |
|
M-A Ruel
2014/10/18 00:47:05
Oops?
iannucci
2014/10/20 21:11:57
Nope, to avoid aliasing p in the func()
M-A Ruel
2014/10/21 00:55:53
It's because you need to call with p as a paramete
| |
| 104 c := c | |
| 105 grp.Add(1) | |
| 106 go func() { | |
| 107 defer grp.Done() | |
| 108 switch c.Mode.Type() { | |
| 109 case "tree": | |
| 110 subtree := r.GetFullTreeID(c.Object.ID(), b, f) | |
| 111 if subtree == nil { | |
| 112 if f == FullTree { | |
| 113 panic(fmt.Errorf("could not load tree %s", c.Object.ID())) | |
| 114 } | |
| 115 } else { | |
| 116 base.children[p] = &Child{subtree, c.Mod e} | |
| 117 } | |
| 118 case "blob": | |
| 119 if b == WithBlobs { | |
| 120 rslt := r.GetObjectID(c.Object.ID()) | |
| 121 if rslt == nil && f == FullTree { | |
| 122 panic(fmt.Errorf("could not load object %s", c.Object.ID())) | |
| 123 } | |
| 124 base.children[p] = &Child{rslt, c.Mode} | |
| 125 } | |
| 126 } | |
| 127 }() | |
| 128 } | |
| 129 grp.Wait() | |
| 130 return base | |
| 131 } | |
| 132 | |
| 133 func (r *Repo) GetFullTreeID(tree ObjectID, b blobOpt, f fullTree) *Tree { | |
| 134 return r.GetFullTree(tree.String(), b, f) | |
| 135 } | |
| 136 | |
| 137 /// Private | |
| 138 | |
| 139 type catFileReply struct { | |
| 140 id ObjectID | |
| 141 typ string | |
| 142 size int | |
| 143 data []byte | |
| 144 } | |
| 145 | |
| 146 type catFileRequest struct { | |
| 147 objectish string | |
| 148 reply chan<- *catFileReply | |
| 149 } | |
| 150 | |
| 151 func (r *Repo) ensureCatFileServer(ch **chan<- catFileRequest, checkOnly bool) { | |
|
M-A Ruel
2014/10/18 00:47:05
Double pointer is unnecessary and you shouldn't no
iannucci
2014/10/20 21:11:57
Come again? How do I safely start the service lazi
| |
| 152 if *ch == nil { | |
| 153 c := make(chan catFileRequest, 16) | |
| 154 swapped := atomic.CompareAndSwapPointer( | |
| 155 (*unsafe.Pointer)(unsafe.Pointer(ch)), | |
| 156 nil, | |
| 157 unsafe.Pointer(&c), | |
| 158 ) | |
| 159 if swapped { | |
| 160 go r.catFileServer(c, checkOnly) | |
| 161 } | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 func (r *Repo) catFileServer(rchan chan catFileRequest, checkOnly bool) { | |
| 166 defer func() { | |
| 167 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&r.catFile) ), nil) | |
| 168 close(rchan) | |
| 169 | |
| 170 if err := recover(); err != nil { | |
| 171 fmt.Println("recovering panick'd catFileServer", err) | |
| 172 } | |
| 173 }() | |
| 174 | |
| 175 arg := "--batch" | |
| 176 if checkOnly { | |
| 177 arg = "--batch-check" | |
| 178 } | |
| 179 catFile := exec.Command("git", "cat-file", arg) | |
| 180 catFile.Dir = r.Path | |
| 181 in, err := catFile.StdinPipe() | |
| 182 if err != nil { | |
| 183 panic(err) | |
|
M-A Ruel
2014/10/18 00:47:05
Send error back in channel
iannucci
2014/10/20 21:11:57
Nowhere to send it to, this condition should never
| |
| 184 } | |
| 185 defer in.Close() | |
| 186 outRaw, err := catFile.StdoutPipe() | |
| 187 if err != nil { | |
| 188 panic(err) | |
| 189 } | |
| 190 defer outRaw.Close() | |
| 191 out := bufio.NewReader(outRaw) | |
| 192 | |
| 193 if err = catFile.Start(); err != nil { | |
| 194 panic(err) | |
| 195 } | |
| 196 | |
| 197 nom := infra_util.Nom(out) | |
| 198 yoink := infra_util.Yoink(out) | |
| 199 | |
| 200 for req := range rchan { | |
| 201 if strings.ContainsAny(req.objectish, "\n") { | |
| 202 panic("catFile request may not contain a newline") | |
| 203 } | |
| 204 | |
| 205 in.Write([]byte(req.objectish + "\n")) | |
| 206 rsp := nom('\n') | |
| 207 if strings.HasSuffix(rsp, " missing") { | |
| 208 req.reply <- nil | |
| 209 continue | |
| 210 } | |
| 211 | |
| 212 parts := strings.Split(rsp, " ") | |
| 213 objID, typ, sizeStr := parts[0], parts[1], parts[2] | |
| 214 size, err := strconv.ParseUint(sizeStr, 10, 64) | |
| 215 if err != nil { | |
| 216 panic(err) | |
| 217 } | |
| 218 | |
| 219 data := []byte{} | |
| 220 if !checkOnly { | |
| 221 data = yoink(int(size)) | |
| 222 out.ReadByte() // drop extra newline | |
| 223 } | |
| 224 req.reply <- &catFileReply{ | |
| 225 id: MakeObjectID(objID), | |
| 226 typ: typ, | |
| 227 data: data, | |
| 228 size: int(size), | |
| 229 } | |
| 230 } | |
| 231 } | |
| OLD | NEW |