git ssb

0+

cel / pull-git-remote-helper



Tree: 76d7bd15ac81df893e05f465fa9200b7c1be2e1e

Files: 76d7bd15ac81df893e05f465fa9200b7c1be2e1e / index.js

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

Built with git-ssb-web