1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
var is = require('is');
var path = require('path');
var crc = require('crc');
var URI = require('urijs');
var pathUtil = require('./path');
var Promise = require('./promise');
var command = require('./command');
var fs = require('./fs');
var GIT_PREFIX = 'git+';
function Git() {
this.tmpDir;
this.cloned = {};
}
// Return an unique ID for a combinaison host/ref
Git.prototype.repoID = function(host, ref) {
return crc.crc32(host+'#'+(ref || '')).toString(16);
};
// Allocate a temporary folder for cloning repos in it
Git.prototype.allocateDir = function() {
var that = this;
if (this.tmpDir) return Promise();
return fs.tmpDir()
.then(function(dir) {
that.tmpDir = dir;
});
};
// Clone a git repository if non existant
Git.prototype.clone = function(host, ref) {
var that = this;
return this.allocateDir()
// Return or clone the git repo
.then(function() {
// Unique ID for repo/ref combinaison
var repoId = that.repoID(host, ref);
// Absolute path to the folder
var repoPath = path.join(that.tmpDir, repoId);
if (that.cloned[repoId]) return repoPath;
// Clone repo
return command.exec('git clone '+host+' '+repoPath)
// Checkout reference if specified
.then(function() {
that.cloned[repoId] = true;
if (!ref) return;
return command.exec('git checkout '+ref, { cwd: repoPath });
})
.thenResolve(repoPath);
});
};
// Get file from a git repo
Git.prototype.resolve = function(giturl) {
// Path to a file in a git repo?
if (!Git.isUrl(giturl)) {
if (this.resolveRoot(giturl)) return Promise(giturl);
return Promise(null);
}
if (is.string(giturl)) giturl = Git.parseUrl(giturl);
if (!giturl) return Promise(null);
// Clone or get from cache
return this.clone(giturl.host, giturl.ref)
.then(function(repo) {
return path.resolve(repo, giturl.filepath);
});
};
// Return root of git repo from a filepath
Git.prototype.resolveRoot = function(filepath) {
var relativeToGit, repoId;
// No git repo cloned, or file is not in a git repository
if (!this.tmpDir || !pathUtil.isInRoot(this.tmpDir, filepath)) return null;
// Extract first directory (is the repo id)
relativeToGit = path.relative(this.tmpDir, filepath);
repoId = relativeToGit.split(path.sep)[0];
if (!repoId) {
return;
}
// Return an absolute file
return path.resolve(this.tmpDir, repoId);
};
// Check if an url is a git dependency url
Git.isUrl = function(giturl) {
return (giturl.indexOf(GIT_PREFIX) === 0);
};
// Parse and extract infos
Git.parseUrl = function(giturl) {
var ref, uri, fileParts, filepath;
if (!Git.isUrl(giturl)) return null;
giturl = giturl.slice(GIT_PREFIX.length);
uri = new URI(giturl);
ref = uri.fragment() || null;
uri.fragment(null);
// Extract file inside the repo (after the .git)
fileParts = uri.path().split('.git');
filepath = fileParts.length > 1? fileParts.slice(1).join('.git') : '';
if (filepath[0] == '/') {
filepath = filepath.slice(1);
}
// Recreate pathname without the real filename
uri.path(fileParts[0] + '.git');
return {
host: uri.toString(),
ref: ref,
filepath: filepath
};
};
module.exports = Git;
|