git ssb

0+

cel / pull-git-remote-helper



Tree: 972b77c21531a8e0c42394380dd3331262f938f9

Files: 972b77c21531a8e0c42394380dd3331262f938f9 / index.js

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

Built with git-ssb-web