├── testgit.js └── git.js /testgit.js: -------------------------------------------------------------------------------- 1 | var git = require("./git"); 2 | 3 | //git.createRepo("/tmp/repos/andris/test", {name: "my-repo", remoteName:"s3"}, function(error){ 4 | // console.log(error || "Success!"); 5 | //}); 6 | 7 | git.createRepo("/tmp/repos/andris/test2", {name: "my-repo", remoteName:"s3"}, function(error){ 8 | console.log(error || "Success!"); 9 | git.pull("/tmp/repos/andris/test2", "s3", function(error){ 10 | console.log(error || "Success!"); 11 | }); 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /git.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), 2 | spawn = require("child_process").spawn; 3 | 4 | var BUCKET = "gitrepo.node.ee"; 5 | 6 | module.exports.createRepo = createRepo; 7 | module.exports.addRemoteRepository = addRemoteRepository; 8 | module.exports.commitAndPush = commitAndPush; 9 | module.exports.pull = pull; 10 | 11 | /** 12 | *

Creates a Git repo to the selected location. If the directory does not exist, 13 | * it is created

14 | * 15 | * @param {String} path Location of the new Git repo 16 | * @param {Object} [options] Options for the repo 17 | * @param {String} [options.name] Repository name 18 | * @param {String} [options.remoteName] Remote repository name 19 | * @param {Function} callback Callback function to run when the function ends 20 | */ 21 | function createRepo(path, options, callback){ 22 | 23 | if(typeof options == "function" && !callback){ 24 | callback = options; 25 | options = undefined; 26 | } 27 | 28 | options = options || {}; 29 | 30 | checkDir(path, function(err){ 31 | if(err){ 32 | return callback(err); 33 | } 34 | 35 | var git = spawn("git", ["init"], {cwd: path}), 36 | errorStr = "", error; 37 | 38 | git.stderr.on('data', function (data) { 39 | errorStr += data.toString("utf-8").trim(); 40 | }); 41 | 42 | git.on("exit", function(code){ 43 | if(code){ 44 | error = new Error(errorStr || ("Git exited with "+code)); 45 | error.code = code; 46 | return callback(error); 47 | } 48 | 49 | if(options.remoteName && options.name){ 50 | addRemoteRepository(path, options, callback); 51 | }else{ 52 | callback(null, true); 53 | } 54 | }); 55 | 56 | }); 57 | } 58 | 59 | function commitAndPush(path, remoteName, callback){ 60 | commit(path, function(error){ 61 | if(error){ 62 | return callback(error); 63 | } 64 | storeMeta(path, function(error){ 65 | var git = spawn("jgit", ["push", remoteName, "master"], {cwd: path}), 66 | errorStr = "", error; 67 | 68 | git.stdout.on('data', function (data) { 69 | console.log(data.toString("utf-8").trim()); 70 | }); 71 | 72 | git.stderr.on('data', function (data) { 73 | errorStr += data.toString("utf-8"); 74 | }); 75 | 76 | git.on("exit", function(code){ 77 | if(code){ 78 | error = new Error((errorStr || ("Git exited with "+code)).trim()); 79 | error.code = code; 80 | return callback(error); 81 | } 82 | 83 | callback(null, true); 84 | }); 85 | }); 86 | }); 87 | } 88 | 89 | function pull(path, remoteName, callback){ 90 | var git = spawn("jgit", ["fetch", remoteName], {cwd: path}), 91 | errorStr = "", error; 92 | 93 | git.stdout.on('data', function (data) { 94 | console.log(data.toString("utf-8").trim()); 95 | }); 96 | 97 | git.stderr.on('data', function (data) { 98 | errorStr += data.toString("utf-8"); 99 | }); 100 | 101 | git.on("exit", function(code){ 102 | if(code){ 103 | error = new Error((errorStr || ("JGit exited with "+code)).trim()); 104 | error.code = code; 105 | return callback(error); 106 | } 107 | 108 | var git = spawn("git", ["merge", remoteName+"/master"], {cwd: path}), 109 | errorStr = "", error; 110 | 111 | git.stdout.on('data', function (data) { 112 | console.log(data.toString("utf-8").trim()); 113 | }); 114 | 115 | git.stderr.on('data', function (data) { 116 | errorStr += data.toString("utf-8"); 117 | }); 118 | 119 | git.on("exit", function(code){ 120 | if(code){ 121 | error = new Error((errorStr || ("Git merge exited with "+code)).trim()); 122 | error.code = code; 123 | return callback(error); 124 | } 125 | 126 | applyMeta(path, function(error){ 127 | if(error){ 128 | console.log(error); 129 | } 130 | callback(null, true); 131 | }); 132 | }); 133 | }); 134 | } 135 | 136 | function commit(path, callback){ 137 | var git = spawn("git", ["add", "-A"], {cwd: path}), 138 | errorStr = "", error; 139 | 140 | git.stdout.on('data', function (data) { 141 | console.log(data.toString("utf-8").trim()); 142 | }); 143 | 144 | git.stderr.on('data', function (data) { 145 | errorStr += data.toString("utf-8").trim(); 146 | }); 147 | 148 | git.on("exit", function(code){ 149 | if(code){ 150 | error = new Error(errorStr || ("git add exited with "+code)); 151 | error.code = code; 152 | return callback(error); 153 | } 154 | 155 | var git = spawn("git", ["commit", "-m", "update"], {cwd: path}), 156 | errorStr = "", error; 157 | 158 | git.stdout.on('data', function (data) { 159 | console.log(data.toString("utf-8").trim()); 160 | }); 161 | 162 | git.stderr.on('data', function (data) { 163 | errorStr += data.toString("utf-8").trim(); 164 | }); 165 | 166 | git.on("exit", function(code){ 167 | if(code){ 168 | error = new Error(errorStr || ("git commit exited with "+code)); 169 | error.code = code; 170 | return callback(error); 171 | } 172 | 173 | callback(null, true); 174 | }); 175 | }); 176 | } 177 | 178 | function storeMeta(path, callback){ 179 | var git = spawn("git-cache-meta", ["--store"], {cwd: path}), 180 | errorStr = "", error; 181 | 182 | git.stderr.on('data', function (data) { 183 | errorStr += data.toString("utf-8").trim(); 184 | }); 185 | 186 | git.on("exit", function(code){ 187 | if(code){ 188 | error = new Error(errorStr || ("git-cache-meta exited with "+code)); 189 | error.code = code; 190 | return callback(error); 191 | } 192 | 193 | callback(null, true); 194 | }); 195 | } 196 | 197 | function applyMeta(path, callback){ 198 | var git = spawn("git-cache-meta", ["--apply"], {cwd: path}), 199 | errorStr = "", error; 200 | 201 | git.stderr.on('data', function (data) { 202 | errorStr += data.toString("utf-8").trim(); 203 | }); 204 | 205 | git.on("exit", function(code){ 206 | if(code){ 207 | error = new Error(errorStr || ("git-cache-meta exited with "+code)); 208 | error.code = code; 209 | return callback(error); 210 | } 211 | 212 | callback(null, true); 213 | }); 214 | } 215 | 216 | /** 217 | *

Adds a S3 remote repository to a repo

218 | * 219 | * @param {String} path Path to the repository 220 | * @param {Object} options Options object 221 | * @param {String} options.name Repository name 222 | * @param {String} options.remoteName Remote repository name 223 | * @param {Function} callback Callback function to run on completion 224 | */ 225 | function addRemoteRepository(path, options, callback){ 226 | var git = spawn("git", ["remote", "add", options.remoteName, "amazon-s3://.jgit@"+BUCKET+"/"+options.name+".git"], {cwd: path}), 227 | errorStr = "", error; 228 | 229 | git.stderr.on('data', function (data) { 230 | errorStr += data.toString("utf-8").trim(); 231 | }); 232 | 233 | git.on("exit", function(code){ 234 | if(code){ 235 | error = new Error(errorStr || ("Git exited with "+code)); 236 | error.code = code; 237 | return callback(error); 238 | } 239 | 240 | callback(null, true); 241 | }); 242 | } 243 | 244 | /** 245 | *

Ensures that a directory exists on selected location, or creates one if needed

246 | * 247 | * @param {String} dir Directory path 248 | * @param {Number} [mode=0666] Directory flags 249 | * @param {Function} callback Callback function to run when the operation succeedes or fails 250 | */ 251 | function checkDir(dir, mode, callback){ 252 | dir = parseDir(dir || ""); 253 | 254 | if(!callback && typeof mode=="function"){ 255 | callback = mode; 256 | mode = undefined; 257 | } 258 | 259 | var created = [], 260 | dirs = dir.split("/"), 261 | fulldir = []; 262 | 263 | if(!dir){ 264 | callback(null, false); 265 | } 266 | 267 | walkDirs(); 268 | 269 | function walkDirs(){ 270 | var curdir; 271 | 272 | if(!dirs.length){ 273 | return callback(null, true); 274 | } 275 | 276 | fulldir.push(dirs.shift()); 277 | curdir = fulldir.join("/"); 278 | 279 | if(!curdir){ 280 | return process.nextTick(walkDirs); 281 | } 282 | 283 | fs.stat(curdir, function(err, stats){ 284 | if(err){ 285 | return createDir(curdir); 286 | } 287 | if(stats.isFile()){ 288 | return cleanOut(new Error(curdir +" is an existing file")) 289 | } 290 | if(stats.isDirectory()){ 291 | return process.nextTick(walkDirs); 292 | } 293 | return cleanOut(new Error(curdir +" status unknown")); 294 | }); 295 | 296 | } 297 | 298 | function createDir(dir){ 299 | fs.mkdir(dir, mode, function(err){ 300 | if(err){ 301 | return cleanOut(err); 302 | }else{ 303 | created.push(dir); 304 | walkDirs(); 305 | } 306 | }); 307 | } 308 | 309 | function cleanOut(err){ 310 | 311 | removeDirs(); 312 | 313 | function removeDirs(){ 314 | var curdir = created.pop(); 315 | if(!curdir){ 316 | return callback(err); 317 | }else{ 318 | fs.rmdir(curdir, removeDirs); 319 | } 320 | } 321 | 322 | } 323 | 324 | } 325 | 326 | /** 327 | *

Resolves any . or .. names from the path

328 | * 329 | * @param {String} dir Directory path 330 | * @return {String} resolved directory path 331 | */ 332 | function parseDir(dir){ 333 | var dirpath = dir.trim().split("/"); 334 | for(var i=0, len = dirpath.length; i0 && dirpath[i-1]){ 338 | dirpath.splice(i-1,2); 339 | len -= 2; 340 | i -= 2; 341 | break; 342 | } 343 | case ".": 344 | dirpath.splice(i,1); 345 | len--; 346 | i--; 347 | break; 348 | } 349 | } 350 | return dirpath.join("/"); 351 | } --------------------------------------------------------------------------------