git ssb

0+

cel / pull-git-remote-helper



Tree: cc32919e93ce91c6d66985c7ca2b78da5ea7f92d

Files: cc32919e93ce91c6d66985c7ca2b78da5ea7f92d / index.js

7391 bytesRaw
1var packCodec = require('js-git/lib/pack-codec')
2var pull = require('pull-stream')
3var cat = require('pull-cat')
4var pushable = require('pull-pushable')
5var buffered = require('pull-buffered')
6
7var options = {
8 verbosity: 1,
9 progress: false
10}
11
12function handleOption(name, value) {
13 switch (name) {
14 case 'verbosity':
15 options.verbosity = +value || 0
16 // console.error("ok verbo")
17 return true
18 case 'progress':
19 options.progress = !!value && value !== 'false'
20 return true
21 default:
22 console.error('unknown option', name + ': ' + value)
23 return false
24 }
25}
26
27function capabilitiesCmd(read) {
28 read(null, function next(end, data) {
29 if(end === true) return
30 if(end) throw end
31
32 console.log(data)
33 read(null, next)
34 })
35}
36
37// return a source that delivers some data and then ends
38// TODO: use pull.once and abortCb for this
39function endSource(data) {
40 var done
41 return function (end, cb) {
42 if (done) return cb(true)
43 done = true
44 cb(null, data)
45 }
46}
47
48function capabilitiesSource(prefix) {
49 return endSource([
50 'option',
51 'connect',
52 'refspec refs/heads/*:refs/' + prefix + '/heads/*',
53 'refspec refs/tags/*:refs/' + prefix + '/tags/*',
54 ].join('\n') + '\n\n')
55}
56
57function split2(str) {
58 var i = str.indexOf(' ')
59 return (i === -1) ? [str, ''] : [
60 str.substr(0, i),
61 str.substr(i + 1)
62 ]
63}
64
65function optionSource(cmd) {
66 var args = split2(cmd)
67 var msg = handleOption(args[0], args[1])
68 msg = (msg === true) ? 'ok'
69 : (msg === false) ? 'unsupported'
70 : 'error ' + msg
71 return endSource(msg + '\n')
72}
73
74function listSource() {
75 return endSource([
76 /* TODO */
77 ].join('\n') + '\n\n')
78}
79
80function createHash() {
81 var hash = crypto.createHash('sha256')
82 var hasher = pull.through(function (data) {
83 hash.update(data)
84 }, function () {
85 hasher.digest = '&'+hash.digest('base64')+'.sha256'
86 })
87 return hasher
88}
89
90function uploadPack(read) {
91 return function (abort, cb) {
92 cb('upload pack not implemented')
93 }
94 // throw new Error('upload pack')
95}
96
97function getRefs() {
98 return pull.values([
99 {
100 hash: '78beaedba9878623cea3862cf18e098cfb901e10',
101 name: 'refs/heads/master'
102 },
103 {
104 hash: '78beaedba9878623cea3862cf18e098cfb901e10',
105 name: 'refs/remotes/cel/master'
106 }
107 ])
108 /*
109 return function (err, cb) {
110 if (err === true) return
111 if (err) throw err
112 // TODO
113 cb(true)
114 }
115 */
116}
117
118function packLineEncode(read) {
119 var ended
120 return function (end, cb) {
121 if (ended) return cb(ended)
122 read(end, function (end, data) {
123 if (ended = end) {
124 cb(end)
125 } else {
126 var len = data ? data.length + 5 : 0
127 cb(end, ('000' + len.toString(16)).substr(-4) + data + '\n')
128 }
129 })
130 }
131}
132
133function packLineDecode(read) {
134 var b = buffered(read)
135 var readPrefix = b.chunks(4)
136 var ended
137
138 b.packLines = function (abort, cb) {
139 readPrefix(abort, function (end, buf) {
140 if (ended = end) return cb(end)
141 var len = parseInt(buf, 16)
142 b.chunks(len)(null, function (end, buf) {
143 if (ended = end) return cb(end)
144 console.error('refline', buf)
145 cb(end, buf)
146 })
147 })
148 }
149
150 return b
151}
152
153/*
154TODO: investigate capabilities
155report-status delete-refs side-band-64k quiet atomic ofs-delta
156*/
157
158// Get a line for each ref that we have. The first line also has capabilities.
159// Wrap with packLineEncode.
160function receivePackHeader(capabilities) {
161 var readRef = getRefs()
162 var first = true
163 var ended
164 return function (abort, cb) {
165 if (ended) return cb(true)
166 readRef(abort, function (end, ref) {
167 ended = end
168 var name = ref && ref.name
169 var hash = ref && ref.hash
170 if (first) {
171 first = false
172 if (end) {
173 // use placeholder data if there are no refs
174 hash = '0000000000000000000000000000000000000000'
175 name = 'capabilities^{}'
176 }
177 name += '\0' + capabilities.join(' ')
178 } else if (end) {
179 return cb(true)
180 }
181 cb(null, hash + ' ' + name)
182 })
183 }
184}
185
186function receiveActualPack(read, objectSink, onEnd) {
187 var objects = pushable()
188 packCodec.decodePack(function (obj) {
189 if (obj) pushable.push(obj)
190 else pushable.end()
191 })
192 objectSink(objects)
193}
194
195function receivePack(read, objectSink) {
196 var ended
197 var sendRefs = receivePackHeader([])
198
199 function receiveRefs(readLine, cb) {
200 var refs = []
201 readLine(null, function next(end, line) {
202 if (end === true)
203 cb(new Error('refs line ended early'))
204 else if (end)
205 cb(end)
206 else if (line === '')
207 cb(null, refs)
208 else {
209 var args = split2(line)
210 refs.push({
211 hash: args[0],
212 name: args[1]
213 })
214 readLine(null, next)
215 }
216 })
217 }
218
219 return packLineEncode(
220 cat([
221 // send our refs
222 sendRefs,
223 pull.once(''),
224 function (abort, cb) {
225 if (abort) return
226 // receive their refs
227 var lines = packLineDecode(read)
228 receiveRefs(lines.packLines, function (err, refs) {
229 if (refs)
230 console.error('refs', refs, err)
231 if (err) return cb(err)
232 // receive the pack
233 receiveActualPack(lines.passthrough, objectSink, function (err) {
234 if (err) return cb(err)
235 cb(true)
236 })
237 })
238 },
239 pull.once('unpack ok')
240 ])
241 )
242}
243
244function prepend(data, read) {
245 var done
246 return function (end, cb) {
247 if (done) {
248 read(end, cb)
249 } else {
250 done = true
251 cb(null, data)
252 }
253 }
254}
255
256module.exports = function (opts) {
257 var ended
258 var prefix = opts.prefix
259 var objectSink = opts.objectSink
260
261 function handleConnect(cmd, read) {
262 var args = split2(cmd)
263 switch (args[0]) {
264 case 'git-upload-pack':
265 return prepend('\n', uploadPack(read))
266 case 'git-receive-pack':
267 return prepend('\n', receivePack(read), objectSink)
268 default:
269 return pull.error(new Error('Unknown service ' + args[0]))
270 }
271 }
272
273 function handleCommand(line, read) {
274 var args = split2(line)
275 switch (args[0]) {
276 case 'capabilities':
277 return capabilitiesSource(prefix)
278 case 'list':
279 return listSource()
280 case 'connect':
281 return handleConnect(args[1], read)
282 case 'option':
283 return optionSource(args[1])
284 default:
285 return pull.error(new Error('Unknown command ' + args[0]))
286 }
287 }
288
289 return function (read) {
290 var b = buffered()
291 b(read)
292 var command
293
294 function getCommand(cb) {
295 b.lines(null, function (end, line) {
296 if (ended = end)
297 return cb(end)
298
299 if (options.verbosity > 1)
300 console.error('command:', line)
301
302 var cmdSource = handleCommand(line, b.passthrough)
303 cb(null, cmdSource)
304 })
305 }
306
307 return function next(abort, cb) {
308 if (ended) return cb(ended)
309
310 if (!command) {
311 if (abort) return
312 getCommand(function (end, cmd) {
313 command = cmd
314 next(end, cb)
315 })
316 return
317 }
318
319 command(abort, function (err, data) {
320 if (err) {
321 command = null
322 if (err !== true)
323 cb(err, data)
324 else
325 next(abort, cb)
326 } else {
327 cb(null, data)
328 }
329 })
330 }
331 }
332}
333
334

Built with git-ssb-web