1 | var util = require('util'),
|
2 | GitObject = require('./git_object').GitObject,
|
3 | fs = require('fs'),
|
4 | LooseStorage = require('./loose_storage').LooseStorage,
|
5 | PackStorage = require('./pack_storage').PackStorage,
|
6 | BinaryParser = require('./binary_parser').BinaryParser;
|
7 |
|
8 | Repository = exports.Repository = function(git_directory, options) {
|
9 | var _git_directory = git_directory;
|
10 | var _options = options ? options : {};
|
11 | var _packs = [];
|
12 | var _loose = null;
|
13 | var _already_searched = {};
|
14 | var self = this;
|
15 |
|
16 | Object.defineProperty(this, "git_directory", { get: function() { return _git_directory; }, set: function(value) { _git_directory = value; }, enumerable: true});
|
17 | Object.defineProperty(this, "options", { get: function() { return _options; }, set: function(value) { _options = value; }, enumerable: true});
|
18 | Object.defineProperty(this, "already_searched", { get: function() { return _already_searched; }, set: function(value) { _already_searched = value; }, enumerable: true});
|
19 | Object.defineProperty(this, "packs", { get: function() { return _packs; }, set: function(value) { _packs = value; }, enumerable: true});
|
20 | Object.defineProperty(this, "loose", { get: function() { return _loose; }, set: function(value) { _loose = value; }, enumerable: true});
|
21 | }
|
22 |
|
23 |
|
24 | var chomp = function chomp(raw_text) {
|
25 | return raw_text.replace(/(\n|\r)+$/, '');
|
26 | }
|
27 |
|
28 | var truncate_array = function(array, sha) {
|
29 |
|
30 | }
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | Repository.prototype.log = function(sha, options, callback) {
|
39 | this.already_searched = {}
|
40 | return walk_log(this, sha, options);
|
41 | }
|
42 |
|
43 | var close = function(repo) {
|
44 | if(repo.packs) {
|
45 | repo.packs.forEach(function(pack) {
|
46 | pack.close();
|
47 | });
|
48 | }
|
49 | }
|
50 |
|
51 | var git_path = function(repo, path) { return repo.git_directory + "/" + path; }
|
52 |
|
53 | var initloose = function(repo) {
|
54 | repo.loaded = [];
|
55 | repo.loose = [];
|
56 | load_loose(repo, git_path(repo, 'objects'));
|
57 | load_alternate_loose(repo, git_path(repo, 'objects'));
|
58 | return repo.loose;
|
59 | }
|
60 |
|
61 | var load_loose = function(repo, path) {
|
62 | repo.loaded.push(path);
|
63 | try {
|
64 | fs.statSync(path);
|
65 | repo.loose.push(new LooseStorage(path));
|
66 | } catch (err) {
|
67 | return;
|
68 | }
|
69 | }
|
70 |
|
71 | var load_alternate_loose = function(repo, path) {
|
72 |
|
73 | var alt = path + '/info/alternates';
|
74 | try {
|
75 | fs.statSync(alt);
|
76 |
|
77 | var lines = fs.readFileSync(alt, 'utf8').split('\n');
|
78 | lines.length > 0 && lines[lines.length - 1] == '' ? lines.pop() : null;
|
79 |
|
80 | lines.forEach(function(line) {
|
81 |
|
82 | if(repo.loaded.indexOf(chomp(line)) == -1) {
|
83 | if(line.substr(0, 2) == "..") {
|
84 | line = fs.realpathSync(repo.git_directory + "/" + line);
|
85 | }
|
86 |
|
87 | load_loose(repo, chomp(line));
|
88 | load_alternate_loose(repo, chomp(line));
|
89 | }
|
90 | });
|
91 | } catch(err) {}
|
92 | }
|
93 |
|
94 | var initpacks = function(repo) {
|
95 | close(repo);
|
96 | repo.loaded_packs = [];
|
97 | repo.packs = [];
|
98 | load_packs(repo, git_path(repo, "objects/pack"));
|
99 | load_alternate_packs(repo, git_path(repo, "objects"));
|
100 | return repo.packs;
|
101 | }
|
102 |
|
103 | var load_packs = function(repo, path) {
|
104 | repo.loaded_packs.push(path);
|
105 | try {
|
106 | fs.statSync(path);
|
107 |
|
108 | fs.readdirSync(path).forEach(function(entry) {
|
109 |
|
110 | if(entry.match(/\.pack$/i)) {
|
111 | var pack = new PackStorage(path + "/" + entry);
|
112 |
|
113 | if(repo.options["map_packfile"]) {
|
114 | pack.cache_objects();
|
115 | }
|
116 |
|
117 | repo.packs.push(pack)
|
118 | }
|
119 | });
|
120 | } catch (err) {
|
121 | }
|
122 | }
|
123 |
|
124 | var load_alternate_packs = function(repo, path) {
|
125 | var alt = path + "/info/alternates";
|
126 |
|
127 | try {
|
128 | fs.statSync(alt);
|
129 |
|
130 | var lines = fs.readFileSync(alt, 'utf8').split('\n');
|
131 | lines.length > 0 && lines[lines.length - 1] == '' ? lines.pop() : null;
|
132 |
|
133 | lines.forEach(function(line) {
|
134 | if(line.substr(0, 2) == "..") {
|
135 | line = fs.realpathSync(repo.git_directory + "/" + line);
|
136 | }
|
137 |
|
138 |
|
139 | var full_pack = chomp(line) + "/pack";
|
140 | if(repo.loaded_packs.indexOf(full_pack) == -1) {
|
141 | load_packs(repo, full_pack);
|
142 | load_alternate_packs(repo, chomp(line));
|
143 | }
|
144 | })
|
145 | } catch(err) {
|
146 | }
|
147 | }
|
148 |
|
149 | var get_raw_object_by_sha1 = function(repo, sha1o) {
|
150 | if(!sha1o || sha1o == "" || sha1o.constructor != String) throw "no such sha found";
|
151 |
|
152 | var sha1 = '';
|
153 | for(var i = 0; i < sha1o.length; i = i + 2) {
|
154 | sha1 = sha1 + String.fromCharCode(parseInt(sha1o.substr(i, 2), 16));
|
155 | }
|
156 |
|
157 | if(!repo.packs) initpacks(repo);
|
158 |
|
159 | var packs = repo.packs;
|
160 | for(var i = 0; i < packs.length; i++) {
|
161 | var o = packs[i].find(sha1);
|
162 | if(o != null) return o;
|
163 | }
|
164 |
|
165 | if(!repo.loose) initloose(repo);
|
166 |
|
167 | var looses = repo.loose;
|
168 | for(var i = 0; i < looses.length; i++) {
|
169 | var o = looses[i].find(sha1);
|
170 | if(o) return o;
|
171 | }
|
172 |
|
173 |
|
174 | initpacks(repo);
|
175 |
|
176 | var packs = repo.packs;
|
177 | for(var i = 0; i < packs.length; i++) {
|
178 | var o = packs[i].find(sha1);
|
179 | if(o != null) return o;
|
180 | }
|
181 |
|
182 |
|
183 | throw "no such sha found";
|
184 | }
|
185 |
|
186 | Repository.prototype.get_object_by_sha1 = function(sha1) {
|
187 | var r = get_raw_object_by_sha1(this, sha1);
|
188 | if(!r) return null;
|
189 | return GitObject.from_raw(r, this);
|
190 | }
|
191 |
|
192 |
|
193 |
|
194 | Repository.prototype.files_changed = function(tree_sha1, tree_sha2, path_limiter) {
|
195 | if(path_limiter == null) return true;
|
196 |
|
197 | var mod = this.quick_diff(tree_sha1, tree_sha2);
|
198 | var files = mod.map(function(c) { return c[0]; });
|
199 | path_limiter = Array.isArray(path_limiter) ? path_limiter : path_limiter != null ? [path_limiter] : [];
|
200 |
|
201 | for(var i = 0; i < path_limiter.length; i++) {
|
202 | if(files.indexOf(path_limiter[i]) != -1) return true;
|
203 | }
|
204 | return false;
|
205 | }
|
206 |
|
207 |
|
208 | Repository.prototype.cat_file = function(sha) {
|
209 | return this.get_object_by_sha1(sha).raw_content;
|
210 | }
|
211 |
|
212 |
|
213 | Repository.prototype.cat_file_size = function(sha) {
|
214 | return get_raw_object_by_sha1(this, sha).content.length;
|
215 | }
|
216 |
|
217 |
|
218 | Repository.prototype.cat_file_type = function(sha) {
|
219 | return get_raw_object_by_sha1(this, sha).type;
|
220 | }
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | Repository.prototype.ls_tree = function(sha, paths, recursive) {
|
227 | var self = this;
|
228 | paths = paths ? paths : [];
|
229 | recursive = recursive ? recursive : false;
|
230 |
|
231 | try {
|
232 | if(paths.length > 0) {
|
233 |
|
234 | var part = [];
|
235 | paths.forEach(function(path) {
|
236 | part = part.concat(self.ls_tree_path(sha, path));
|
237 | })
|
238 |
|
239 | return part.join("\n");
|
240 | } else {
|
241 | return this.get_raw_tree(sha, recursive);
|
242 | }
|
243 | } catch (err) {
|
244 | return '';
|
245 | }
|
246 | }
|
247 |
|
248 | Repository.prototype.get_raw_tree = function(sha, recursive) {
|
249 | var self = this;
|
250 | recursive = recursive ? recursive : false;
|
251 | var tree = null;
|
252 |
|
253 | var o = get_raw_object_by_sha1(this, sha);
|
254 | if(o.type == 'commit') {
|
255 | tree = this.get_object_by_sha1(sha).tree;
|
256 | } else if(o.type == 'tag') {
|
257 | var commit_sha = this.get_object_by_sha1(sha).object;
|
258 | tree = this.get_object_by_sha1(commit_sha).tree;
|
259 | } else if(o.type == 'tree') {
|
260 | tree = sha;
|
261 | } else {
|
262 | return null;
|
263 | }
|
264 |
|
265 |
|
266 | return recursive ? this.get_raw_trees(tree) : this.cat_file(tree);
|
267 | }
|
268 |
|
269 |
|
270 |
|
271 | Repository.prototype.get_raw_trees = function(sha, path) {
|
272 | var self = this;
|
273 | path = path ? path : '';
|
274 | var out = '';
|
275 |
|
276 | this.cat_file(sha).split('\n').forEach(function(line) {
|
277 | var parts = line.split(/\s/);
|
278 | var mode = parts[0], type = parts[1], sha = parts[2], name = parts[3];
|
279 |
|
280 | if(type == 'tree') {
|
281 | var full_name = path.length == 0 ? name : (path + '/' + name);
|
282 | out = out + self.get_raw_trees(sha, full_name);
|
283 | } else if(path.length == 0) {
|
284 | out = out + line + '\n';
|
285 | } else {
|
286 | out = out + line.replace(new RegExp(name, 'g'), (path + '/' + name)) + '\n';
|
287 | }
|
288 | });
|
289 |
|
290 | return out;
|
291 | }
|
292 |
|
293 |
|
294 |
|
295 | Repository.prototype.ls_tree_path = function(sha, path, append) {
|
296 | var self = this;
|
297 | var tree = this.get_raw_tree(sha);
|
298 |
|
299 | if(path.match(/\//)) {
|
300 | var paths = path.split('/');
|
301 | paths.length > 0 && paths[paths.length - 1] == '' ? paths.pop() : null;
|
302 | var last = path.substr(path.length - 1, 1);
|
303 |
|
304 | if((last == '/') && (paths.length == 1)) {
|
305 | var append = append ? (append + "/" + paths[0]) : paths[0];
|
306 | var dir_name = tree.split('\n').filter(function(p) { return p.split('\t')[1] == paths[0]; })[0];
|
307 |
|
308 | if(dir_name == null) throw "no such path";
|
309 | var next_sha = dir_name.split(/ |\t/)[2];
|
310 | var tree = self.get_raw_tree(next_sha);
|
311 |
|
312 | tree = tree.split('\n');
|
313 |
|
314 | if(append) {
|
315 | var mod_tree = [];
|
316 | tree.forEach(function(ent) {
|
317 | var parts = ent.split('\t');
|
318 | var info = parts[0], fpath = parts[1];
|
319 | mod_tree.push([info, (append + "/" + fpath)].join('\t'));
|
320 | });
|
321 | return mod_tree;
|
322 | } else {
|
323 | return tree;
|
324 | }
|
325 | } else {
|
326 | if(tree == null) throw "no such path";
|
327 | var next_path = paths.shift();
|
328 | var dir_name = tree.split('\n').filter(function(p) { return p.split('\t')[1] == next_path; })[0];
|
329 | if(dir_name == null) throw "no such path";
|
330 | var next_sha = dir_name.split(/ |\t/)[2];
|
331 | next_path = append ? (append + "/" + next_path) : next_path;
|
332 |
|
333 | if(last == '/') {
|
334 | return self.ls_tree_path(next_sha, (paths.join('/') + '/'), next_path);
|
335 | } else {
|
336 | return self.ls_tree_path(next_sha, paths.join('/'), next_path);
|
337 | }
|
338 | }
|
339 | } else {
|
340 | if(tree == null) throw "no such path";
|
341 | var tree = tree.split('\n');
|
342 | tree = tree.filter(function(p) { return p.split('\t')[1] == path; });
|
343 |
|
344 | if(append) {
|
345 | var mod_tree = [];
|
346 | tree.forEach(function(ent) {
|
347 | var parts = ent.split('\t');
|
348 | var info = parts[0], fpath = parts[1];
|
349 | mod_tree.push([info, (append + '/' + fpath)].join('\t'));
|
350 | });
|
351 | return mod_tree;
|
352 | } else {
|
353 | return tree;
|
354 | }
|
355 | }
|
356 | }
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | Repository.prototype.quick_diff = function(tree1, tree2, path, recurse) {
|
366 | var self = this;
|
367 | path = path ? path : '.';
|
368 | recurse = recurse ? recurse : true;
|
369 |
|
370 | var changed = [];
|
371 | if(tree1 == tree2) return changed;
|
372 |
|
373 | var t1 = tree1 ? this.list_tree(tree1) : null;
|
374 | var t2 = tree2 ? this.list_tree(tree2) : null;
|
375 |
|
376 |
|
377 | if(t1) {
|
378 | Object.keys(t1['blob']).forEach(function(file) {
|
379 | var hsh = t1['blob'][file];
|
380 |
|
381 | var t2_file = t2 ? t2['blob'][file] : null;
|
382 | var full = path + "/" + file;
|
383 | if(!t2_file) {
|
384 | changed.push([full, 'added', hsh['sha'], null]);
|
385 | } else if(hsh['sha'] != t2_file['sha']) {
|
386 | changed.push([full, 'modified', hsh['sha'], t2_file['sha']]);
|
387 | }
|
388 | });
|
389 | }
|
390 |
|
391 |
|
392 |
|
393 | if(t2) {
|
394 | Object.keys(t2['blob']).forEach(function(file) {
|
395 | var hsh = t2 ? t2['blob'][file] : null;
|
396 | if(t1 == null || t1['blob'][file] == null) {
|
397 | changed.push([path + "/" + file, 'removed', null, hsh['sha']]);
|
398 | }
|
399 | });
|
400 | }
|
401 |
|
402 |
|
403 | if(t1) {
|
404 | Object.keys(t1['tree']).forEach(function(dir) {
|
405 | var hsh = t1['tree'][dir];
|
406 | var t2_tree = t2 ? t2['tree'][dir] : null;
|
407 | var full = path + "/" + dir;
|
408 |
|
409 | if(!t2_tree) {
|
410 | if(recurse) {
|
411 | changed = changed.concat(self.quick_diff(hsh['sha'], null, full, true));
|
412 | } else {
|
413 | changed.push([full, 'added', hsh['sha', null]]);
|
414 | }
|
415 | } else if(hsh['sha'] != t2_tree['sha']) {
|
416 | if(recurse) {
|
417 | changed = changed.concat(self.quick_diff(hsh['sha'], t2_tree['sha'], full, true));
|
418 | } else {
|
419 | changed.push([full, 'modified', hsh['sha'], t2_tree['sha']]);
|
420 | }
|
421 | }
|
422 | });
|
423 | }
|
424 |
|
425 | if(t2) {
|
426 | Object.keys(t2['tree']).forEach(function(dir) {
|
427 | var hsh = t2['tree'][dir];
|
428 | var t1_tree = t1 ? t1['tree'][dir] : null;
|
429 |
|
430 | if(!t1_tree) {
|
431 | if(recurse) {
|
432 | changed = changed.concat(self.quick_diff(null, hsh['sha'], full, true));
|
433 | } else {
|
434 | changed.push([full, 'removed', null, hsh['sha']]);
|
435 | }
|
436 | }
|
437 | });
|
438 | }
|
439 |
|
440 | return changed;
|
441 | }
|
442 |
|
443 |
|
444 |
|
445 |
|
446 | Repository.prototype.list_tree = function(sha) {
|
447 | var data = {blob:{}, tree:{}, link:{}, commit:{}};
|
448 | var object = this.get_object_by_sha1(sha);
|
449 | object.entries.forEach(function(entry) {
|
450 | data[entry.format_type][entry.name] = {mode:entry.format_type, sha:entry.sha1};
|
451 | });
|
452 |
|
453 | return data;
|
454 | }
|
455 |
|
456 | var walk_log = function(repo, sha, options, total_size) {
|
457 | if(total_size == null) total_size = 0;
|
458 | if(repo.already_searched[sha]) return [];
|
459 |
|
460 | repo.already_searched[sha] = true;
|
461 |
|
462 | var array = [];
|
463 | var o = null, commit_sha = null, c = null, output = null;
|
464 |
|
465 | if(sha) {
|
466 |
|
467 | o = get_raw_object_by_sha1(repo, sha);
|
468 |
|
469 |
|
470 | if(o.type == "tag") {
|
471 | commit_sha = repo.get_object_by_sha1(sha).object;
|
472 | c = repo.get_object_by_sha1(commit_sha);
|
473 | } else {
|
474 | c = GitObject.from_raw(o, repo);
|
475 | }
|
476 |
|
477 |
|
478 | if(c.type != "commit") return [];
|
479 |
|
480 |
|
481 | var add_sha = true;
|
482 |
|
483 | if(options["since"] && (options["since"] && options["since"].constructor == Date) && (options["since"] > c.committer.date)) {
|
484 | add_sha = false;
|
485 | }
|
486 | if(options["until"] && (options["until"] && options["until"].constructor == Date) && (options["until"] < c.committer.date)) {
|
487 | add_sha = false;
|
488 | }
|
489 |
|
490 |
|
491 | var subarray = [];
|
492 |
|
493 | if(c.parent.length == 0 && options["path_limiter"]) {
|
494 | add_sha = false;
|
495 | }
|
496 |
|
497 | if(options["max_count"] == null || ((array.length + total_size) < options["max_count"])) {
|
498 | if(options["path_limiter"] == null) {
|
499 | output = c.raw_log(sha);
|
500 | array.push([sha, output, c.committer.date]);
|
501 | }
|
502 |
|
503 | if(options["max_count"] != null && (array.length + total_size) >= options["max_count"]) {
|
504 | return array;
|
505 | }
|
506 |
|
507 | for(var i = 0; i < c.parent.length; i++) {
|
508 | var psha = c.parent[i];
|
509 | var tree = repo.get_object_by_sha1(psha).tree;
|
510 |
|
511 | if(psha && !repo.files_changed(c.tree, tree, options["path_limiter"])) {
|
512 | add_sha = false;
|
513 | }
|
514 |
|
515 |
|
516 | var results = walk_log(repo, psha, options, (array.length + total_size));
|
517 | subarray = subarray.concat(results);
|
518 | if(options["first_parent"]) break;
|
519 | }
|
520 |
|
521 | if(options["path_limiter"] != null && add_sha) {
|
522 | output = c.raw_log(sha);
|
523 | array.push([sha, output, c.comitter.date]);
|
524 | }
|
525 |
|
526 | if(add_sha) {
|
527 | array = array.concat(subarray);
|
528 | }
|
529 | }
|
530 | }
|
531 |
|
532 | return array;
|
533 | }
|
534 |
|
535 | var convert = function(d) {
|
536 | return (
|
537 | d.constructor === Date ? d :
|
538 | d.constructor === Array ? new Date(d[0],d[1],d[2]) :
|
539 | d.constructor === Number ? new Date(d) :
|
540 | d.constructor === String ? new Date(d) :
|
541 | typeof d === "object" ? new Date(d.year,d.month,d.date) :
|
542 | NaN
|
543 | );
|
544 | }
|
545 |
|
546 | var compare = function(a,b) {
|
547 | return (
|
548 | isFinite(a=convert(a).valueOf()) &&
|
549 | isFinite(b=convert(b).valueOf()) ?
|
550 | (a>b)-(a<b) :
|
551 | NaN
|
552 | );
|
553 | }
|
554 |
|
555 | Repository.prototype.rev_list = function(sha, options, callback) {
|
556 | try {
|
557 | var end_sha = null;
|
558 |
|
559 | if(Array.isArray(sha)) {
|
560 | end_sha = sha[0], sha = sha[1];
|
561 | }
|
562 |
|
563 |
|
564 | var log = this.log(sha, options);
|
565 |
|
566 | log = log.sort(function(a, b) {
|
567 | return compare(a[2], b[2])
|
568 | }).reverse();
|
569 |
|
570 |
|
571 | if(end_sha) {
|
572 | log = truncate_arr(log, end_sha);
|
573 | }
|
574 |
|
575 |
|
576 | if(options['max_count']) {
|
577 | var opt_len = parseInt(options['max_count']);
|
578 |
|
579 | if(opt_len < log.length) {
|
580 | log = log.slice(0, opt_len);
|
581 | }
|
582 | }
|
583 |
|
584 |
|
585 | if(options['pretty'] == 'raw') {
|
586 | log = log.map(function(log_entry) { return log_entry[1]; }).join("");
|
587 | } else {
|
588 | log = log.map(function(log_entry) { return log_entry[0]; }).join("\n");
|
589 | }
|
590 |
|
591 |
|
592 | callback(null, log);
|
593 | } catch (err) {
|
594 | callback(err, null);
|
595 | }
|
596 | }
|
597 |
|
598 |
|
599 | var truncate_arr = function(arr, end_sha) {
|
600 | var new_arr = [];
|
601 |
|
602 | for(var i = 0; i < arr.length; i++) {
|
603 | var a = arr[i];
|
604 | if(a[0] == sha) {
|
605 | return new_arr;
|
606 | }
|
607 | new_arr.push(a);
|
608 | }
|
609 | return new_arr;
|
610 | }
|
611 |
|
612 |
|
613 | Repository.prototype.object_exists = function(sha1, callback) {
|
614 | var self = this;
|
615 | var sha_hex = '';
|
616 | for(var i = 0; i < sha1.length; i = i + 2) {
|
617 | sha_hex = sha_hex + String.fromCharCode(parseInt(sha1.substr(i, 2), 16));
|
618 | }
|
619 |
|
620 |
|
621 | self.in_packs(sha_hex, function(err, result) {
|
622 | if(err) return callback(err, result);
|
623 | if(result) return callback(null, result);
|
624 |
|
625 |
|
626 | self.in_loose(sha_hex, function(err, result) {
|
627 | if(err) return callback(err, result);
|
628 | if(result) return callback(null, result);
|
629 |
|
630 |
|
631 | initpacks(self);
|
632 |
|
633 | self.in_packs(sha_hex, function(err, result) {
|
634 | if(err) return callback(err, result);
|
635 | callback(null, result);
|
636 | });
|
637 | });
|
638 | })
|
639 | }
|
640 |
|
641 |
|
642 | Repository.prototype.in_packs = function(sha_hex, callback) {
|
643 |
|
644 | var packs = this.packs;
|
645 | for(var i = 0; i < packs.length; i++) {
|
646 | var o = packs[i].find(sha_hex);
|
647 | if(o != null) return callback(null, true);
|
648 | }
|
649 | callback(null, false);
|
650 | }
|
651 |
|
652 |
|
653 | Repository.prototype.in_loose = function(sha_hex, callback) {
|
654 | if(!this.loose) initloose(this);
|
655 |
|
656 | var looses = this.loose;
|
657 | for(var i = 0; i < looses.length; i++) {
|
658 | var o = looses[i].find(sha_hex);
|
659 | if(o) return callback(null, true);
|
660 | }
|
661 | callback(null, false);
|
662 | }
|
663 |
|
664 |
|
665 | Repository.prototype.get_subtree = function(commit_sha, path, callback) {
|
666 | var self = this;
|
667 |
|
668 | var tree_sha = this.get_object_by_sha1(commit_sha).tree;
|
669 |
|
670 | if(path && !(path == '' || path == '.' || path == './')) {
|
671 | var paths = path.split('/');
|
672 |
|
673 | for(var i = 0; i < paths.length; i++) {
|
674 |
|
675 | path = paths[i];
|
676 |
|
677 | if(paths[i] != '') {
|
678 | var tree = this.get_object_by_sha1(tree_sha);
|
679 | var entry = tree.entries.filter(function(e) { return e.name == path; }).shift();
|
680 |
|
681 | if(entry) {
|
682 | tree_sha = entry.sha1;
|
683 | } else {
|
684 | return callback('no subtree located for ' + commit_sha, null);
|
685 | }
|
686 | }
|
687 | }
|
688 | }
|
689 |
|
690 | callback(null, tree_sha);
|
691 | }
|
692 |
|
693 | Repository.init = function(dir, bare, callback) {
|
694 | try {
|
695 | var args = Array.prototype.slice.call(arguments, 0);
|
696 | callback = args.pop();
|
697 | dir = args.length ? args.shift() : true;
|
698 | bare = args.length ? args.shift() : true;
|
699 |
|
700 |
|
701 | try { fs.statSync(dir); } catch(err) { fs.mkdirSync(dir, 16877); }
|
702 |
|
703 | try { fs.statSync(dir + "/objects"); return callback(null, false) } catch(err) {};
|
704 |
|
705 | create_initial_config(dir, bare);
|
706 |
|
707 | fs.mkdirSync(dir + "/refs", 16877);
|
708 | fs.mkdirSync(dir + "/refs/heads", 16877);
|
709 | fs.mkdirSync(dir + "/refs/tags", 16877);
|
710 | fs.mkdirSync(dir + "/refs/info", 16877);
|
711 | fs.mkdirSync(dir + "/refs/pack", 16877);
|
712 | fs.mkdirSync(dir + "/branches", 16877);
|
713 |
|
714 | add_file(dir, 'description', 'Unnamed repository; edit this file to name it for gitweb.');
|
715 | add_file(dir, 'HEAD', 'ref: refs/heads/master\n');
|
716 |
|
717 | fs.mkdirSync(dir + "/hooks", 16877);
|
718 |
|
719 | add_file(dir + "/hooks", 'applypatch-msg', '# add shell script and make executable to enable');
|
720 | add_file(dir + "/hooks", 'post-commit', '# add shell script and make executable to enable');
|
721 | add_file(dir + "/hooks", 'post-receive', '# add shell script and make executable to enable');
|
722 | add_file(dir + "/hooks", 'post-update', '# add shell script and make executable to enable');
|
723 | add_file(dir + "/hooks", 'pre-applypatch', '# add shell script and make executable to enable');
|
724 | add_file(dir + "/hooks", 'pre-commit', '# add shell script and make executable to enable');
|
725 | add_file(dir + "/hooks", 'pre-rebase', '# add shell script and make executable to enable');
|
726 | add_file(dir + "/hooks", 'update', '# add shell script and make executable to enable');
|
727 |
|
728 | fs.mkdirSync(dir + "/info", 16877);
|
729 | add_file(dir, 'info/exclude', "# *.[oa]\n# *~");
|
730 | callback(null, self);
|
731 | } catch(err) {
|
732 | callback(err, null);
|
733 | }
|
734 | }
|
735 |
|
736 | var create_initial_config = function(dir, bare) {
|
737 | var bare_status = bare ? 'true' : 'false';
|
738 | var config = "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = " + bare_status + "\n\tlogallrefupdates = true";
|
739 | add_file(dir, 'config', config);
|
740 | }
|
741 |
|
742 | var add_file = function(dir, name, content) {
|
743 | fs.writeFileSync(dir + "/" + name, content);
|
744 | }
|
745 |
|
746 |
|
747 | Repository.prototype.put_raw_object = function(content, type, callback) {
|
748 | return this.loose[0].put_raw_object(content, type, callback);
|
749 | }
|
750 |
|
751 |
|
752 |
|
753 |
|
754 |
|
755 |
|
756 |
|
757 |
|
758 |
|
759 |
|
760 |
|
761 |
|
762 |
|
763 |
|
764 |
|