UNPKG

24.1 kBJavaScriptView Raw
1var 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
8Repository = 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// Chomp text removing end carriage returns
24var chomp = function chomp(raw_text) {
25 return raw_text.replace(/(\n|\r)+$/, '');
26}
27
28var truncate_array = function(array, sha) {
29
30}
31
32// takes the following options:
33// :since - Time object specifying that you don't want commits BEFORE this
34// :until - Time object specifying that you don't want commit AFTER this
35// :first_parent - tells log to only walk first parent
36// :path_limiter - string or array of strings to limit path
37// :max_count - number to limit the output
38Repository.prototype.log = function(sha, options, callback) {
39 this.already_searched = {}
40 return walk_log(this, sha, options);
41}
42
43var close = function(repo) {
44 if(repo.packs) {
45 repo.packs.forEach(function(pack) {
46 pack.close();
47 });
48 }
49}
50
51var git_path = function(repo, path) { return repo.git_directory + "/" + path; }
52
53var 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
61var 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
71var load_alternate_loose = function(repo, path) {
72 // load alternate loose too
73 var alt = path + '/info/alternates';
74 try {
75 fs.statSync(alt);
76 // Read and process all entries in the directory
77 var lines = fs.readFileSync(alt, 'utf8').split('\n');
78 lines.length > 0 && lines[lines.length - 1] == '' ? lines.pop() : null;
79 // Iterate over alternate loose storage locations
80 lines.forEach(function(line) {
81 // Only load the path once
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
94var 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
103var load_packs = function(repo, path) {
104 repo.loaded_packs.push(path);
105 try {
106 fs.statSync(path);
107 // Read and process all entries in the directory
108 fs.readdirSync(path).forEach(function(entry) {
109 // If we have a pack file create a new storage object
110 if(entry.match(/\.pack$/i)) {
111 var pack = new PackStorage(path + "/" + entry);
112 // If we have specified the map for the pack then load the entire object map
113 if(repo.options["map_packfile"]) {
114 pack.cache_objects();
115 }
116 // Add pack to list of packs in the repo
117 repo.packs.push(pack)
118 }
119 });
120 } catch (err) {
121 }
122}
123
124var load_alternate_packs = function(repo, path) {
125 var alt = path + "/info/alternates";
126
127 try {
128 fs.statSync(alt);
129 // Read and process all entries in the directory
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 // Get pack file name
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
149var 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 // Init packs if we have none set yet
157 if(!repo.packs) initpacks(repo);
158 // Try packs
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 // Try loose storage
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 // try packs again maybe the object got packed in the meantime
174 initpacks(repo);
175 // Try packs
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 // No results throw an error that no sha pack object was found
183 throw "no such sha found";
184}
185
186Repository.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// returns true if the files in the path_limiter were changed or no path limiter
193// used by the log() function when passed with a path_limiter
194Repository.prototype.files_changed = function(tree_sha1, tree_sha2, path_limiter) {
195 if(path_limiter == null) return true;
196 // We got a path limiter, let's perform the diff to check for changes
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// Returns the raw file contents of this sha
208Repository.prototype.cat_file = function(sha) {
209 return this.get_object_by_sha1(sha).raw_content;
210}
211
212// Returns the file size (as an int) of this sha
213Repository.prototype.cat_file_size = function(sha) {
214 return get_raw_object_by_sha1(this, sha).content.length;
215}
216
217// Returns the file type as string of this sha
218Repository.prototype.cat_file_type = function(sha) {
219 return get_raw_object_by_sha1(this, sha).type;
220}
221
222// returns the raw (cat-file) output for a tree
223// if given a commit sha, it will print the tree of that commit
224// if given a path limiter array, it will limit the output to those
225// if asked for recursive trees, will traverse trees
226Repository.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 // pathing
234 var part = [];
235 paths.forEach(function(path) {
236 part = part.concat(self.ls_tree_path(sha, path));
237 })
238 // Return the parts
239 return part.join("\n");
240 } else {
241 return this.get_raw_tree(sha, recursive);
242 }
243 } catch (err) {
244 return '';
245 }
246}
247
248Repository.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 // If recursive execute next level of trees otherwise return the raw file
266 return recursive ? this.get_raw_trees(tree) : this.cat_file(tree);
267}
268
269// Grabs tree contents recursively,
270// e.g. `git ls-tree -r sha`
271Repository.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 // Return the out
290 return out;
291}
292
293// return array of tree entries
294// TODO : refactor this to remove the fugly
295Repository.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// takes 2 tree shas and recursively walks them to find out what
359// files or directories have been modified in them and returns on
360// array of changes
361// [ [full_path, 'added', tree1_hash, nil],
362// [full_path, 'removed', nil, tree2_hash],
363// [full_path, 'modified', tree1_hash, tree2_hash]
364// ]
365Repository.prototype.quick_diff = function(tree1, tree2, path, recurse) {
366 var self = this;
367 path = path ? path : '.';
368 recurse = recurse ? recurse : true;
369 // Handle empty trees
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 // Check that we have tree 1 blob differences
377 if(t1) {
378 Object.keys(t1['blob']).forEach(function(file) {
379 var hsh = t1['blob'][file];
380 // Fetch the same file in tree 2
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]); // not in parent
385 } else if(hsh['sha'] != t2_file['sha']) {
386 changed.push([full, 'modified', hsh['sha'], t2_file['sha']]); // file changed
387 }
388 });
389 }
390
391
392 // Check tree 2 blobs
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 // Check for all the tree objects
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 // Return all the changed files
440 return changed;
441}
442
443// returna 2-d hash of the tree
444// ['blob']['FILENAME'] = {:mode => '100644', :sha => SHA}
445// ['tree']['DIRNAME'] = {:mode => '040000', :sha => SHA}
446Repository.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
456var walk_log = function(repo, sha, options, total_size) {
457 if(total_size == null) total_size = 0;
458 if(repo.already_searched[sha]) return [];
459 // Add the sha to the list of allready searched for sha's
460 repo.already_searched[sha] = true;
461 // Empty array
462 var array = [];
463 var o = null, commit_sha = null, c = null, output = null;
464
465 if(sha) {
466 // Get the raw object
467 o = get_raw_object_by_sha1(repo, sha);
468
469 // Create a git object from the raw object
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 // If it is not a commit
478 if(c.type != "commit") return [];
479
480 // Add sha
481 var add_sha = true;
482 // Check if the commit should be in the results
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 // Follow all parents unless --first-parent is specified
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 // Walk the next level of the tree
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 // Return all the commits
532 return array;
533}
534
535var 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
546var 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
555Repository.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 // Walk the log
564 var log = this.log(sha, options);
565 // Sort the log
566 log = log.sort(function(a, b) {
567 return compare(a[2], b[2])
568 }).reverse();
569
570 // Truncate array
571 if(end_sha) {
572 log = truncate_arr(log, end_sha);
573 }
574
575 // Shorten the list if it's longer than max_count
576 if(options['max_count']) {
577 var opt_len = parseInt(options['max_count']);
578 // If the length is less than the total log
579 if(opt_len < log.length) {
580 log = log.slice(0, opt_len);
581 }
582 }
583
584 // Pretty print the result if option is specified
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 // Return the log
592 callback(null, log);
593 } catch (err) {
594 callback(err, null);
595 }
596}
597
598// Cut off the array at a specific point
599var 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// Returns true/false if that sha exists in the db
613Repository.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 // Search in the packs
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 // Search in loose
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 // Search again in the packs after an init in case it appeared in the meantime
631 initpacks(self);
632 // Search in the packs
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// Returns true if the hex-packed sha is in the packfile
642Repository.prototype.in_packs = function(sha_hex, callback) {
643 // Try packs
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// Returns true if the hex-packed sha is in the loose objects
653Repository.prototype.in_loose = function(sha_hex, callback) {
654 if(!this.loose) initloose(this);
655 // Try loose storage
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// Get a subtree
665Repository.prototype.get_subtree = function(commit_sha, path, callback) {
666 var self = this;
667 // Fetch tree sha
668 var tree_sha = this.get_object_by_sha1(commit_sha).tree;
669 // Ensure we have a valid path
670 if(path && !(path == '' || path == '.' || path == './')) {
671 var paths = path.split('/');
672
673 for(var i = 0; i < paths.length; i++) {
674 // Get the path element
675 path = paths[i];
676 // Ignore empty paths
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 // Return the tree_sha
690 callback(null, tree_sha);
691}
692
693Repository.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 // Create the directory if it does not exist
701 try { fs.statSync(dir); } catch(err) { fs.mkdirSync(dir, 16877); }
702 // Check if we are allready initialized
703 try { fs.statSync(dir + "/objects"); return callback(null, false) } catch(err) {};
704 // The directory does not exist so let's create it
705 create_initial_config(dir, bare);
706 // Create all the directories
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 // Add basic files
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 // Create hooks directory
717 fs.mkdirSync(dir + "/hooks", 16877);
718 // Add empty shell scripts
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 // Create info directory
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
736var 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
742var add_file = function(dir, name, content) {
743 fs.writeFileSync(dir + "/" + name, content);
744}
745
746// writes a raw object into the git repo
747Repository.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