git ssb

0+

cel / pull-git-remote-helper



Tree: 89810d797532892ef70d5ee23f3b82c5466e7858

Files: 89810d797532892ef70d5ee23f3b82c5466e7858 / index.js

7434 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 cb(end, buf)
145 })
146 })
147 }
148
149 return b
150}
151
152/*
153TODO: investigate capabilities
154report-status delete-refs side-band-64k quiet atomic ofs-delta
155*/
156
157// Get a line for each ref that we have. The first line also has capabilities.
158// Wrap with packLineEncode.
159function receivePackHeader(capabilities) {
160 var readRef = getRefs()
161 var first = true
162 var ended
163 return function (abort, cb) {
164 if (ended) return cb(true)
165 readRef(abort, function (end, ref) {
166 ended = end
167 var name = ref && ref.name
168 var hash = ref && ref.hash
169 if (first) {
170 first = false
171 if (end) {
172 // use placeholder data if there are no refs
173 hash = '0000000000000000000000000000000000000000'
174 name = 'capabilities^{}'
175 }
176 name += '\0' + capabilities.join(' ')
177 } else if (end) {
178 return cb(true)
179 }
180 cb(null, hash + ' ' + name)
181 })
182 }
183}
184
185function receiveActualPack(read, objectSink, onEnd) {
186 var objects = pushable()
187 packCodec.decodePack(function (obj) {
188 if (obj) pushable.push(obj)
189 else pushable.end()
190 })
191 objectSink(objects)
192}
193
194function receivePack(read, objectSink) {
195 var ended
196 var sendRefs = receivePackHeader([])
197
198 function receiveRefs(readLine, cb) {
199 var refs = []
200 readLine(null, function next(end, line) {
201 /*
202 if (end === true)
203 cb(new Error('refs line ended early'))
204 else */
205 if (end)
206 cb(end)
207 else if (line === '')
208 cb(null, refs)
209 else {
210 var args = split2(line)
211 refs.push({
212 hash: args[0],
213 name: args[1]
214 })
215 readLine(null, next)
216 }
217 })
218 }
219
220 return packLineEncode(
221 cat([
222 // send our refs
223 sendRefs,
224 pull.once(''),
225 function (abort, cb) {
226 if (abort) return
227 // receive their refs
228 var lines = packLineDecode(read)
229 receiveRefs(lines.packLines, function (err, refs) {
230 if (refs)
231 console.error('refs', refs, err)
232 if (err) return cb(err)
233 // receive the pack
234 receiveActualPack(lines.passthrough, objectSink, function (err) {
235 if (err) return cb(err)
236 cb(true)
237 })
238 })
239 },
240 pull.once('unpack ok')
241 ])
242 )
243}
244
245function prepend(data, read) {
246 var done
247 return function (end, cb) {
248 if (done) {
249 read(end, cb)
250 } else {
251 done = true
252 cb(null, data)
253 }
254 }
255}
256
257module.exports = function (opts) {
258 var ended
259 var prefix = opts.prefix
260 var objectSink = opts.objectSink
261
262 function handleConnect(cmd, read) {
263 var args = split2(cmd)
264 switch (args[0]) {
265 case 'git-upload-pack':
266 return prepend('\n', uploadPack(read))
267 case 'git-receive-pack':
268 return prepend('\n', receivePack(read), objectSink)
269 default:
270 return pull.error(new Error('Unknown service ' + args[0]))
271 }
272 }
273
274 function handleCommand(line, read) {
275 var args = split2(line)
276 switch (args[0]) {
277 case 'capabilities':
278 return capabilitiesSource(prefix)
279 case 'list':
280 return listSource()
281 case 'connect':
282 return handleConnect(args[1], read)
283 case 'option':
284 return optionSource(args[1])
285 default:
286 return pull.error(new Error('Unknown command ' + line))
287 }
288 }
289
290 return function (read) {
291 var b = buffered()
292 b(read)
293 var command
294
295 function getCommand(cb) {
296 b.lines(null, function next(end, line) {
297 if (ended = end)
298 return cb(end)
299
300 if (line == '')
301 return b.lines(null, next)
302
303 if (options.verbosity > 1)
304 console.error('command:', line)
305
306 var cmdSource = handleCommand(line, b.passthrough)
307 cb(null, cmdSource)
308 })
309 }
310
311 return function next(abort, cb) {
312 if (ended) return cb(ended)
313
314 if (!command) {
315 if (abort) return
316 getCommand(function (end, cmd) {
317 command = cmd
318 next(end, cb)
319 })
320 return
321 }
322
323 command(abort, function (err, data) {
324 if (err) {
325 command = null
326 if (err !== true)
327 cb(err, data)
328 else
329 next(abort, cb)
330 } else {
331 cb(null, data)
332 }
333 })
334 }
335 }
336}
337
338

Built with git-ssb-web