pj-nodegit 0.18.4 → 0.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/README.md +3 -0
- package/lib/attr.js +20 -0
- package/lib/blame.js +20 -0
- package/lib/blob.js +73 -0
- package/lib/branch.js +19 -0
- package/lib/buf.js +11 -0
- package/lib/checkout.js +51 -0
- package/lib/cherrypick.js +73 -0
- package/lib/clone.js +33 -0
- package/lib/commit.js +437 -0
- package/lib/config.js +25 -0
- package/lib/convenient_hunks.js +61 -0
- package/lib/convenient_patch.js +131 -0
- package/lib/credential.js +33 -0
- package/lib/diff.js +113 -0
- package/lib/diff_file.js +38 -0
- package/lib/diff_line.js +32 -0
- package/lib/enums.js +689 -0
- package/lib/error.js +17 -0
- package/lib/filter_registry.js +25 -0
- package/lib/index.js +103 -0
- package/lib/libgit2.js +6 -0
- package/lib/merge.js +72 -0
- package/lib/nodegit.js +1333 -0
- package/lib/note.js +17 -0
- package/lib/object.js +45 -0
- package/lib/odb_object.js +9 -0
- package/lib/oid.js +23 -0
- package/lib/rebase.js +142 -0
- package/lib/reference.js +213 -0
- package/lib/remote.js +270 -0
- package/lib/repository.js +1982 -0
- package/lib/reset.js +76 -0
- package/lib/revert.js +77 -0
- package/lib/revwalk.js +142 -0
- package/lib/signature.js +38 -0
- package/lib/stash.js +62 -0
- package/lib/status.js +18 -0
- package/lib/status_file.js +106 -0
- package/lib/status_list.js +12 -0
- package/lib/submodule.js +51 -0
- package/lib/tag.js +135 -0
- package/lib/tree.js +175 -0
- package/lib/tree_entry.js +99 -0
- package/lib/utils/lookup_wrapper.js +39 -0
- package/lib/utils/normalize_fetch_options.js +43 -0
- package/lib/utils/normalize_options.js +29 -0
- package/lib/utils/shallow_clone.js +14 -0
- package/package.json +1 -1
@@ -0,0 +1,1982 @@
|
|
1
|
+
var fse = require("fs-extra");
|
2
|
+
var fp = require("lodash/fp");
|
3
|
+
var NodeGit = require("../");
|
4
|
+
var Blob = NodeGit.Blob;
|
5
|
+
var Checkout = NodeGit.Checkout;
|
6
|
+
var Commit = NodeGit.Commit;
|
7
|
+
var normalizeOptions = NodeGit.Utils.normalizeOptions;
|
8
|
+
var shallowClone = NodeGit.Utils.shallowClone;
|
9
|
+
var path = require("path");
|
10
|
+
var Filter = NodeGit.Filter;
|
11
|
+
var FilterList = NodeGit.FilterList;
|
12
|
+
var Reference = NodeGit.Reference;
|
13
|
+
var Remote = NodeGit.Remote;
|
14
|
+
var Repository = NodeGit.Repository;
|
15
|
+
var Revwalk = NodeGit.Revwalk;
|
16
|
+
var Status = NodeGit.Status;
|
17
|
+
var StatusFile = NodeGit.StatusFile;
|
18
|
+
var StatusList = NodeGit.StatusList;
|
19
|
+
var Submodule = NodeGit.Submodule;
|
20
|
+
var Tag = NodeGit.Tag;
|
21
|
+
var Tree = NodeGit.Tree;
|
22
|
+
var TreeBuilder = NodeGit.Treebuilder;
|
23
|
+
|
24
|
+
var _discover = Repository.discover;
|
25
|
+
var _initExt = Repository.initExt;
|
26
|
+
var _fetchheadForeach = Repository.prototype.fetchheadForeach;
|
27
|
+
var _mergeheadForeach = Repository.prototype.mergeheadForeach;
|
28
|
+
|
29
|
+
function applySelectedLinesToTarget
|
30
|
+
(originalContent, newLines, pathHunks, isStaged, reverse) {
|
31
|
+
// 43: ascii code for '+'
|
32
|
+
// 45: ascii code for '-'
|
33
|
+
var lineTypes = {
|
34
|
+
ADDED: !reverse ? 43 : 45,
|
35
|
+
DELETED: !reverse ? 45 : 43
|
36
|
+
};
|
37
|
+
var newContent = "";
|
38
|
+
var oldIndex = 0;
|
39
|
+
var linesPromises = [];
|
40
|
+
|
41
|
+
var oldLines = originalContent.toString().split("\n");
|
42
|
+
|
43
|
+
// if no selected lines were sent, return the original content
|
44
|
+
if (!newLines || newLines.length === 0) {
|
45
|
+
return originalContent;
|
46
|
+
}
|
47
|
+
|
48
|
+
function lineEqualsFirstNewLine(hunkLine) {
|
49
|
+
return ((hunkLine.oldLineno() === newLines[0].oldLineno()) &&
|
50
|
+
(hunkLine.newLineno() === newLines[0].newLineno()));
|
51
|
+
}
|
52
|
+
|
53
|
+
function processSelectedLine(hunkLine) {
|
54
|
+
// if this hunk line is a selected line find the selected line
|
55
|
+
var newLine = newLines.filter(function(nLine) {
|
56
|
+
return ((hunkLine.oldLineno() === nLine.oldLineno()) &&
|
57
|
+
(hunkLine.newLineno() === nLine.newLineno()));
|
58
|
+
});
|
59
|
+
|
60
|
+
if (hunkLine.content().indexOf("\") !== -1) {
|
61
|
+
return false;
|
62
|
+
}
|
63
|
+
|
64
|
+
// determine what to add to the new content
|
65
|
+
if ((isStaged && newLine && newLine.length > 0) ||
|
66
|
+
(!isStaged && (!newLine || newLine.length === 0))) {
|
67
|
+
if (hunkLine.origin() !== lineTypes.ADDED) {
|
68
|
+
newContent += hunkLine.content();
|
69
|
+
}
|
70
|
+
if ((isStaged && hunkLine.origin() !== lineTypes.DELETED) ||
|
71
|
+
(!isStaged && hunkLine.origin() !== lineTypes.ADDED)) {
|
72
|
+
oldIndex++;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
else {
|
76
|
+
switch (hunkLine.origin()) {
|
77
|
+
case lineTypes.ADDED:
|
78
|
+
newContent += hunkLine.content();
|
79
|
+
if (isStaged) {
|
80
|
+
oldIndex++;
|
81
|
+
}
|
82
|
+
break;
|
83
|
+
case lineTypes.DELETED:
|
84
|
+
if (!isStaged) {
|
85
|
+
oldIndex++;
|
86
|
+
}
|
87
|
+
break;
|
88
|
+
default:
|
89
|
+
newContent += oldLines[oldIndex++];
|
90
|
+
if (oldIndex < oldLines.length) {
|
91
|
+
newContent += "\n";
|
92
|
+
}
|
93
|
+
break;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
// find the affected hunk
|
99
|
+
pathHunks.forEach(function(pathHunk) {
|
100
|
+
linesPromises.push(pathHunk.lines());
|
101
|
+
});
|
102
|
+
|
103
|
+
return Promise.all(linesPromises).then(function(results) {
|
104
|
+
for (var i = 0; i < results.length && newContent.length < 1; i++) {
|
105
|
+
var hunkStart = isStaged || reverse ? pathHunks[i].newStart()
|
106
|
+
: pathHunks[i].oldStart();
|
107
|
+
var lines = results[i];
|
108
|
+
if (lines.filter(lineEqualsFirstNewLine).length > 0) {
|
109
|
+
// add content that is before the hunk
|
110
|
+
while (hunkStart > (oldIndex + 1)) {
|
111
|
+
newContent += oldLines[oldIndex++] + "\n";
|
112
|
+
}
|
113
|
+
|
114
|
+
// modify the lines of the hunk according to the selection
|
115
|
+
lines.forEach(processSelectedLine);
|
116
|
+
|
117
|
+
// add the rest of the file
|
118
|
+
while (oldLines.length > oldIndex) {
|
119
|
+
newContent += oldLines[oldIndex++] +
|
120
|
+
(oldLines.length > oldIndex ? "\n" : "");
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
return newContent;
|
126
|
+
});
|
127
|
+
}
|
128
|
+
|
129
|
+
function getPathHunks(repo, index, filePath, isStaged, additionalDiffOptions) {
|
130
|
+
var diffOptions = additionalDiffOptions ? {
|
131
|
+
flags: additionalDiffOptions
|
132
|
+
} : undefined;
|
133
|
+
|
134
|
+
return Promise.resolve()
|
135
|
+
.then(function() {
|
136
|
+
if (isStaged) {
|
137
|
+
return repo.getHeadCommit()
|
138
|
+
.then(function getTreeFromCommit(commit) {
|
139
|
+
return commit.getTree();
|
140
|
+
})
|
141
|
+
.then(function getDiffFromTree(tree) {
|
142
|
+
return NodeGit.Diff.treeToIndex(repo, tree, index, diffOptions);
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
146
|
+
return NodeGit.Diff.indexToWorkdir(repo, index, {
|
147
|
+
flags:
|
148
|
+
NodeGit.Diff.OPTION.SHOW_UNTRACKED_CONTENT |
|
149
|
+
NodeGit.Diff.OPTION.RECURSE_UNTRACKED_DIRS |
|
150
|
+
(additionalDiffOptions || 0)
|
151
|
+
});
|
152
|
+
})
|
153
|
+
.then(function(diff) {
|
154
|
+
return NodeGit.Status.file(repo, filePath)
|
155
|
+
.then(function(status) {
|
156
|
+
if (!(status & NodeGit.Status.STATUS.WT_MODIFIED) &&
|
157
|
+
!(status & NodeGit.Status.STATUS.INDEX_MODIFIED)) {
|
158
|
+
return Promise.reject
|
159
|
+
("Selected staging is only available on modified files.");
|
160
|
+
}
|
161
|
+
return diff.patches();
|
162
|
+
});
|
163
|
+
})
|
164
|
+
.then(function(patches) {
|
165
|
+
var pathPatch = patches.filter(function(patch) {
|
166
|
+
return patch.newFile().path() === filePath;
|
167
|
+
});
|
168
|
+
|
169
|
+
if (pathPatch.length !== 1) {
|
170
|
+
return Promise.reject("No differences found for this file.");
|
171
|
+
}
|
172
|
+
|
173
|
+
return pathPatch[0].hunks();
|
174
|
+
});
|
175
|
+
}
|
176
|
+
|
177
|
+
function getReflogMessageForCommit(commit) {
|
178
|
+
var parentCount = commit.parentcount();
|
179
|
+
var summary = commit.summary();
|
180
|
+
var commitType;
|
181
|
+
|
182
|
+
if (parentCount >= 2) {
|
183
|
+
commitType = " (merge)";
|
184
|
+
} else if (parentCount == 0) {
|
185
|
+
commitType = " (initial)";
|
186
|
+
} else {
|
187
|
+
commitType = "";
|
188
|
+
}
|
189
|
+
|
190
|
+
return `commit${commitType}: ${summary}`;
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* Goes through a rebase's rebase operations and commits them if there are
|
195
|
+
* no merge conflicts
|
196
|
+
*
|
197
|
+
* @param {Repository} repository The repository that the rebase is being
|
198
|
+
* performed in
|
199
|
+
* @param {Rebase} rebase The current rebase being performed
|
200
|
+
* @param {Signature} signature Identity of the one performing the rebase
|
201
|
+
* @param {Function} beforeNextFn Callback to be called before each
|
202
|
+
* invocation of next(). If the callback
|
203
|
+
* returns a promise, the next() will be
|
204
|
+
* called when the promise resolves.
|
205
|
+
* @param {Function} beforeFinishFn Callback called before the invocation
|
206
|
+
* of finish(). If the callback returns a
|
207
|
+
* promise, finish() will be called when the
|
208
|
+
* promise resolves. This callback will be
|
209
|
+
* provided a detailed overview of the rebase
|
210
|
+
* @return {Int|Index} An error code for an unsuccesful rebase or an index for
|
211
|
+
* a rebase with conflicts
|
212
|
+
*/
|
213
|
+
function performRebase(
|
214
|
+
repository,
|
215
|
+
rebase,
|
216
|
+
signature,
|
217
|
+
beforeNextFn,
|
218
|
+
beforeFinishFn
|
219
|
+
) {
|
220
|
+
var beforeNextFnResult;
|
221
|
+
|
222
|
+
/* In the case of FF merges and a beforeFinishFn, this will fail
|
223
|
+
* when looking for 'rewritten' so we need to handle that case.
|
224
|
+
*/
|
225
|
+
function readRebaseMetadataFile(fileName, continueOnError) {
|
226
|
+
return fse.readFile(
|
227
|
+
path.join(repository.path(), "rebase-merge", fileName),
|
228
|
+
{ encoding: "utf8" }
|
229
|
+
)
|
230
|
+
.then(fp.trim)
|
231
|
+
.catch(function(err) {
|
232
|
+
if (continueOnError) {
|
233
|
+
return null;
|
234
|
+
}
|
235
|
+
throw err;
|
236
|
+
});
|
237
|
+
}
|
238
|
+
|
239
|
+
function calcHeadName(input) {
|
240
|
+
return input.replace(/refs\/heads\/(.*)/, "$1");
|
241
|
+
}
|
242
|
+
|
243
|
+
function getPromise() {
|
244
|
+
return rebase.next()
|
245
|
+
.then(function() {
|
246
|
+
return repository.refreshIndex();
|
247
|
+
})
|
248
|
+
.then(function(index) {
|
249
|
+
if (index.hasConflicts()) {
|
250
|
+
throw index;
|
251
|
+
}
|
252
|
+
|
253
|
+
return rebase.commit(null, signature);
|
254
|
+
})
|
255
|
+
.then(function() {
|
256
|
+
|
257
|
+
return performRebase(
|
258
|
+
repository,
|
259
|
+
rebase,
|
260
|
+
signature,
|
261
|
+
beforeNextFn,
|
262
|
+
beforeFinishFn
|
263
|
+
);
|
264
|
+
})
|
265
|
+
.catch(function(error) {
|
266
|
+
if (error && error.errno === NodeGit.Error.CODE.ITEROVER) {
|
267
|
+
const calcRewritten = fp.cond([
|
268
|
+
[fp.isEmpty, fp.constant(null)],
|
269
|
+
[fp.stubTrue, fp.flow([
|
270
|
+
fp.split("\n"),
|
271
|
+
fp.map(fp.split(" "))
|
272
|
+
])]
|
273
|
+
]);
|
274
|
+
|
275
|
+
const beforeFinishFnPromise = !beforeFinishFn ?
|
276
|
+
Promise.resolve() :
|
277
|
+
Promise.all([
|
278
|
+
readRebaseMetadataFile("onto_name"),
|
279
|
+
readRebaseMetadataFile("onto"),
|
280
|
+
readRebaseMetadataFile("head-name").then(calcHeadName),
|
281
|
+
readRebaseMetadataFile("orig-head"),
|
282
|
+
readRebaseMetadataFile("rewritten", true).then(calcRewritten)
|
283
|
+
])
|
284
|
+
.then(function([
|
285
|
+
ontoName,
|
286
|
+
ontoSha,
|
287
|
+
originalHeadName,
|
288
|
+
originalHeadSha,
|
289
|
+
rewritten
|
290
|
+
]) {
|
291
|
+
return beforeFinishFn({
|
292
|
+
ontoName,
|
293
|
+
ontoSha,
|
294
|
+
originalHeadName,
|
295
|
+
originalHeadSha,
|
296
|
+
rebase,
|
297
|
+
rewritten
|
298
|
+
});
|
299
|
+
});
|
300
|
+
|
301
|
+
return beforeFinishFnPromise
|
302
|
+
.then(function() {
|
303
|
+
return rebase.finish(signature);
|
304
|
+
});
|
305
|
+
} else {
|
306
|
+
throw error;
|
307
|
+
}
|
308
|
+
});
|
309
|
+
}
|
310
|
+
|
311
|
+
if(beforeNextFn) {
|
312
|
+
beforeNextFnResult = beforeNextFn(rebase);
|
313
|
+
// if beforeNextFn returns a promise, chain the promise
|
314
|
+
return Promise.resolve(beforeNextFnResult)
|
315
|
+
.then(getPromise);
|
316
|
+
}
|
317
|
+
|
318
|
+
return getPromise();
|
319
|
+
}
|
320
|
+
|
321
|
+
/**
|
322
|
+
* Creates a branch with the passed in name pointing to the commit
|
323
|
+
*
|
324
|
+
* @async
|
325
|
+
* @param {String} startPath The base path where the lookup starts.
|
326
|
+
* @param {Number} acrossFs If non-zero, then the lookup will not stop when a
|
327
|
+
filesystem device change is detected while exploring
|
328
|
+
parent directories.
|
329
|
+
* @param {String} ceilingDirs A list of absolute symbolic link free paths.
|
330
|
+
the search will stop if any of these paths
|
331
|
+
are hit. This may be set to null
|
332
|
+
* @return {String} Path of the git repository
|
333
|
+
*/
|
334
|
+
Repository.discover = function(startPath, acrossFs, ceilingDirs) {
|
335
|
+
return _discover(startPath, acrossFs, ceilingDirs)
|
336
|
+
.then(function(foundPath) {
|
337
|
+
return path.resolve(foundPath);
|
338
|
+
});
|
339
|
+
};
|
340
|
+
|
341
|
+
// Override Repository.initExt to normalize initoptions
|
342
|
+
Repository.initExt = function(repo_path, opts) {
|
343
|
+
opts = normalizeOptions(opts, NodeGit.RepositoryInitOptions);
|
344
|
+
return _initExt(repo_path, opts);
|
345
|
+
};
|
346
|
+
|
347
|
+
|
348
|
+
Repository.getReferences = function(repo, type, refNamesOnly) {
|
349
|
+
return repo.getReferences().then(function(refList) {
|
350
|
+
var filteredRefList = refList.filter(function(reference) {
|
351
|
+
return type === Reference.TYPE.ALL || reference.type === type;
|
352
|
+
});
|
353
|
+
|
354
|
+
if (refNamesOnly) {
|
355
|
+
return filteredRefList.map(function(reference) {
|
356
|
+
return reference.name();
|
357
|
+
});
|
358
|
+
}
|
359
|
+
|
360
|
+
return filteredRefList;
|
361
|
+
});
|
362
|
+
};
|
363
|
+
|
364
|
+
/**
|
365
|
+
* This will set the HEAD to point to the local branch and then attempt
|
366
|
+
* to update the index and working tree to match the content of the
|
367
|
+
* latest commit on that branch
|
368
|
+
*
|
369
|
+
* @async
|
370
|
+
* @param {String|Reference} branch the branch to checkout
|
371
|
+
* @param {Object|CheckoutOptions} opts the options to use for the checkout
|
372
|
+
*/
|
373
|
+
Repository.prototype.checkoutBranch = function(branch, opts) {
|
374
|
+
var repo = this;
|
375
|
+
|
376
|
+
return repo.getReference(branch)
|
377
|
+
.then(function(ref) {
|
378
|
+
if (!ref.isBranch()) {
|
379
|
+
return false;
|
380
|
+
}
|
381
|
+
return repo.checkoutRef(ref, opts);
|
382
|
+
});
|
383
|
+
};
|
384
|
+
|
385
|
+
/**
|
386
|
+
* This will set the HEAD to point to the reference and then attempt
|
387
|
+
* to update the index and working tree to match the content of the
|
388
|
+
* latest commit on that reference
|
389
|
+
*
|
390
|
+
* @async
|
391
|
+
* @param {Reference} reference the reference to checkout
|
392
|
+
* @param {Object|CheckoutOptions} opts the options to use for the checkout
|
393
|
+
*/
|
394
|
+
Repository.prototype.checkoutRef = function(reference, opts) {
|
395
|
+
var repo = this;
|
396
|
+
opts = opts || {};
|
397
|
+
|
398
|
+
opts.checkoutStrategy = opts.checkoutStrategy ||
|
399
|
+
(NodeGit.Checkout.STRATEGY.SAFE |
|
400
|
+
NodeGit.Checkout.STRATEGY.RECREATE_MISSING);
|
401
|
+
return repo.getReferenceCommit(reference.name())
|
402
|
+
.then(function(commit) {
|
403
|
+
return commit.getTree();
|
404
|
+
})
|
405
|
+
.then(function(tree) {
|
406
|
+
return Checkout.tree(repo, tree, opts);
|
407
|
+
})
|
408
|
+
.then(function() {
|
409
|
+
var name = reference.name();
|
410
|
+
return repo.setHead(name);
|
411
|
+
});
|
412
|
+
};
|
413
|
+
|
414
|
+
/**
|
415
|
+
* Continues an existing rebase
|
416
|
+
*
|
417
|
+
* @async
|
418
|
+
* @param {Signature} signature Identity of the one performing the rebase
|
419
|
+
* @param {Function} beforeNextFn Callback to be called before each step
|
420
|
+
* of the rebase. If the callback returns a
|
421
|
+
* promise, the rebase will resume when the
|
422
|
+
* promise resolves. The rebase object is
|
423
|
+
* is passed to the callback.
|
424
|
+
* @param {Function} beforeFinishFn Callback called before the invocation
|
425
|
+
* of finish(). If the callback returns a
|
426
|
+
* promise, finish() will be called when the
|
427
|
+
* promise resolves. This callback will be
|
428
|
+
* provided a detailed overview of the rebase
|
429
|
+
* @param {RebaseOptions} rebaseOptions Options to initialize the rebase object
|
430
|
+
* with
|
431
|
+
* @return {Oid|Index} A commit id for a succesful merge or an index for a
|
432
|
+
* rebase with conflicts
|
433
|
+
*/
|
434
|
+
Repository.prototype.continueRebase = function(
|
435
|
+
signature,
|
436
|
+
beforeNextFn,
|
437
|
+
beforeFinishFn,
|
438
|
+
rebaseOptions
|
439
|
+
) {
|
440
|
+
const repo = this;
|
441
|
+
|
442
|
+
let rebase;
|
443
|
+
let promiseChain = Promise.resolve();
|
444
|
+
|
445
|
+
if (!signature) {
|
446
|
+
promiseChain = promiseChain
|
447
|
+
.then(() => repo.defaultSignature())
|
448
|
+
.then((signatureResult) => {
|
449
|
+
signature = signatureResult;
|
450
|
+
});
|
451
|
+
}
|
452
|
+
|
453
|
+
return promiseChain
|
454
|
+
.then(() => repo.refreshIndex())
|
455
|
+
.then((index) => {
|
456
|
+
if (index.hasConflicts()) {
|
457
|
+
throw index;
|
458
|
+
}
|
459
|
+
|
460
|
+
return NodeGit.Rebase.open(repo, rebaseOptions);
|
461
|
+
})
|
462
|
+
.then((_rebase) => {
|
463
|
+
rebase = _rebase;
|
464
|
+
return rebase.commit(null, signature)
|
465
|
+
.catch((e) => {
|
466
|
+
// If the first commit on continueRebase is a
|
467
|
+
// "patch already applied" error,
|
468
|
+
// interpret that as an explicit "skip commit"
|
469
|
+
// and ignore the error.
|
470
|
+
const errno = fp.get(["errno"], e);
|
471
|
+
if (errno === NodeGit.Error.CODE.EAPPLIED) {
|
472
|
+
return;
|
473
|
+
}
|
474
|
+
|
475
|
+
throw e;
|
476
|
+
});
|
477
|
+
})
|
478
|
+
.then(() => {
|
479
|
+
return performRebase(
|
480
|
+
repo,
|
481
|
+
rebase,
|
482
|
+
signature,
|
483
|
+
beforeNextFn,
|
484
|
+
beforeFinishFn
|
485
|
+
);
|
486
|
+
})
|
487
|
+
.then((error) => {
|
488
|
+
if (error) {
|
489
|
+
throw error;
|
490
|
+
}
|
491
|
+
|
492
|
+
return repo.getBranchCommit("HEAD");
|
493
|
+
});
|
494
|
+
};
|
495
|
+
|
496
|
+
/**
|
497
|
+
* Creates a branch with the passed in name pointing to the commit
|
498
|
+
*
|
499
|
+
* @async
|
500
|
+
* @param {String} name Branch name, e.g. "master"
|
501
|
+
* @param {Commit|String|Oid} commit The commit the branch will point to
|
502
|
+
* @param {Boolean} force Overwrite branch if it exists
|
503
|
+
* @return {Reference}
|
504
|
+
*/
|
505
|
+
Repository.prototype.createBranch = function(name, commit, force) {
|
506
|
+
var repo = this;
|
507
|
+
|
508
|
+
if (commit instanceof Commit) {
|
509
|
+
return NodeGit.Branch.create(
|
510
|
+
repo,
|
511
|
+
name,
|
512
|
+
commit,
|
513
|
+
force ? 1 : 0);
|
514
|
+
}
|
515
|
+
else {
|
516
|
+
return repo.getCommit(commit).then(function(commit) {
|
517
|
+
return NodeGit.Branch.create(
|
518
|
+
repo,
|
519
|
+
name,
|
520
|
+
commit,
|
521
|
+
force ? 1 : 0);
|
522
|
+
});
|
523
|
+
}
|
524
|
+
};
|
525
|
+
|
526
|
+
/**
|
527
|
+
* Create a blob from a buffer
|
528
|
+
*
|
529
|
+
* @async
|
530
|
+
* @param {Buffer} buffer
|
531
|
+
* @return {Oid}
|
532
|
+
*/
|
533
|
+
Repository.prototype.createBlobFromBuffer = function(buffer) {
|
534
|
+
return Blob.createFromBuffer(this, buffer, buffer.length);
|
535
|
+
};
|
536
|
+
|
537
|
+
/**
|
538
|
+
* Create a commit
|
539
|
+
*
|
540
|
+
* @async
|
541
|
+
* @param {String} updateRef
|
542
|
+
* @param {Signature} author
|
543
|
+
* @param {Signature} committer
|
544
|
+
* @param {String} message
|
545
|
+
* @param {Oid|String} Tree
|
546
|
+
* @param {Array} parents
|
547
|
+
* @return {Oid} The oid of the commit
|
548
|
+
*/
|
549
|
+
Repository.prototype.createCommit = function(
|
550
|
+
updateRef, author, committer, message, tree, parents) {
|
551
|
+
|
552
|
+
var repo = this;
|
553
|
+
var promises = [];
|
554
|
+
|
555
|
+
parents = parents || [];
|
556
|
+
|
557
|
+
promises.push(repo.getTree(tree));
|
558
|
+
|
559
|
+
parents.forEach(function(parent) {
|
560
|
+
promises.push(repo.getCommit(parent));
|
561
|
+
});
|
562
|
+
|
563
|
+
return Promise.all(promises).then(function(results) {
|
564
|
+
tree = results[0];
|
565
|
+
|
566
|
+
// Get the normalized values for our input into the function
|
567
|
+
var parentsLength = parents.length;
|
568
|
+
parents = [];
|
569
|
+
|
570
|
+
for (var i = 0; i < parentsLength; i++) {
|
571
|
+
parents.push(results[i + 1]);
|
572
|
+
}
|
573
|
+
|
574
|
+
return Commit.create(
|
575
|
+
repo,
|
576
|
+
updateRef,
|
577
|
+
author,
|
578
|
+
committer,
|
579
|
+
null /* use default message encoding */,
|
580
|
+
message,
|
581
|
+
tree,
|
582
|
+
parents.length,
|
583
|
+
parents
|
584
|
+
);
|
585
|
+
});
|
586
|
+
};
|
587
|
+
|
588
|
+
/**
|
589
|
+
* Create a commit
|
590
|
+
*
|
591
|
+
* @async
|
592
|
+
* @param {Signature} author
|
593
|
+
* @param {Signature} committer
|
594
|
+
* @param {String} message
|
595
|
+
* @param {Oid|String} treeOid
|
596
|
+
* @param {Array} parents
|
597
|
+
* @return {String} The content of the commit object
|
598
|
+
* as a string
|
599
|
+
*/
|
600
|
+
Repository.prototype.createCommitBuffer = function(
|
601
|
+
author, committer, message, treeOid, parents) {
|
602
|
+
|
603
|
+
const repo = this;
|
604
|
+
const promises = (parents || [])
|
605
|
+
.reduce(function(acc, parent) {
|
606
|
+
acc.push(repo.getCommit(parent));
|
607
|
+
return acc;
|
608
|
+
}, [repo.getTree(treeOid)]);
|
609
|
+
|
610
|
+
return Promise.all(promises)
|
611
|
+
.then(function([tree, ...parentCommits]) {
|
612
|
+
return Commit.createBuffer(
|
613
|
+
repo,
|
614
|
+
author,
|
615
|
+
committer,
|
616
|
+
null /* use default message encoding */,
|
617
|
+
message,
|
618
|
+
tree,
|
619
|
+
parentCommits.length,
|
620
|
+
parentCommits
|
621
|
+
);
|
622
|
+
});
|
623
|
+
};
|
624
|
+
|
625
|
+
/**
|
626
|
+
* Create a commit that is digitally signed
|
627
|
+
*
|
628
|
+
* @async
|
629
|
+
* @param {String} updateRef
|
630
|
+
* @param {Signature} author
|
631
|
+
* @param {Signature} committer
|
632
|
+
* @param {String} message
|
633
|
+
* @param {Tree|Oid|String} Tree
|
634
|
+
* @param {Array} parents
|
635
|
+
* @param {Function} onSignature Callback to be called with string to be signed
|
636
|
+
* @return {Oid} The oid of the commit
|
637
|
+
*/
|
638
|
+
Repository.prototype.createCommitWithSignature = function(
|
639
|
+
updateRef,
|
640
|
+
author,
|
641
|
+
committer,
|
642
|
+
message,
|
643
|
+
tree,
|
644
|
+
parents,
|
645
|
+
onSignature
|
646
|
+
) {
|
647
|
+
|
648
|
+
var repo = this;
|
649
|
+
var promises = [];
|
650
|
+
var commitContent;
|
651
|
+
var skippedSigning;
|
652
|
+
|
653
|
+
parents = parents || [];
|
654
|
+
|
655
|
+
promises.push(repo.getTree(tree));
|
656
|
+
|
657
|
+
parents.forEach(function(parent) {
|
658
|
+
promises.push(repo.getCommit(parent));
|
659
|
+
});
|
660
|
+
|
661
|
+
const createCommitPromise = Promise.all(promises).then(function(results) {
|
662
|
+
tree = results[0];
|
663
|
+
|
664
|
+
// Get the normalized values for our input into the function
|
665
|
+
var parentsLength = parents.length;
|
666
|
+
parents = [];
|
667
|
+
|
668
|
+
for (var i = 0; i < parentsLength; i++) {
|
669
|
+
parents.push(results[i + 1]);
|
670
|
+
}
|
671
|
+
|
672
|
+
return Commit.createBuffer(
|
673
|
+
repo,
|
674
|
+
author,
|
675
|
+
committer,
|
676
|
+
null /* use default message encoding */,
|
677
|
+
message,
|
678
|
+
tree,
|
679
|
+
parents.length,
|
680
|
+
parents
|
681
|
+
);
|
682
|
+
}).then(function(commitContentResult) {
|
683
|
+
commitContent = commitContentResult;
|
684
|
+
if (!commitContent.endsWith("\n")) {
|
685
|
+
commitContent += "\n";
|
686
|
+
}
|
687
|
+
return onSignature(commitContent);
|
688
|
+
}).then(function({ code, field, signedData }) {
|
689
|
+
switch (code) {
|
690
|
+
case NodeGit.Error.CODE.OK:
|
691
|
+
return Commit.createWithSignature(
|
692
|
+
repo,
|
693
|
+
commitContent,
|
694
|
+
signedData,
|
695
|
+
field
|
696
|
+
);
|
697
|
+
case NodeGit.Error.CODE.PASSTHROUGH:
|
698
|
+
skippedSigning = true;
|
699
|
+
return Commit.create(
|
700
|
+
repo,
|
701
|
+
updateRef,
|
702
|
+
author,
|
703
|
+
committer,
|
704
|
+
null /* use default message encoding */,
|
705
|
+
message,
|
706
|
+
tree,
|
707
|
+
parents.length,
|
708
|
+
parents
|
709
|
+
);
|
710
|
+
default: {
|
711
|
+
const error = new Error(
|
712
|
+
"Repository.prototype.createCommitWithSignature " +
|
713
|
+
`threw with error code ${code}`
|
714
|
+
);
|
715
|
+
error.errno = code;
|
716
|
+
throw error;
|
717
|
+
}
|
718
|
+
}
|
719
|
+
});
|
720
|
+
|
721
|
+
if (!updateRef) {
|
722
|
+
return createCommitPromise;
|
723
|
+
}
|
724
|
+
|
725
|
+
return createCommitPromise
|
726
|
+
.then(function(commitOid) {
|
727
|
+
if (skippedSigning) {
|
728
|
+
return commitOid;
|
729
|
+
}
|
730
|
+
|
731
|
+
return repo.getCommit(commitOid)
|
732
|
+
.then(function(commitResult) {
|
733
|
+
return Reference.updateTerminal(
|
734
|
+
repo,
|
735
|
+
updateRef,
|
736
|
+
commitOid,
|
737
|
+
getReflogMessageForCommit(commitResult),
|
738
|
+
committer
|
739
|
+
);
|
740
|
+
})
|
741
|
+
.then(function() {
|
742
|
+
return commitOid;
|
743
|
+
});
|
744
|
+
});
|
745
|
+
};
|
746
|
+
|
747
|
+
/**
|
748
|
+
* Creates a new commit on HEAD from the list of passed in files
|
749
|
+
*
|
750
|
+
* @async
|
751
|
+
* @param {Array} filesToAdd
|
752
|
+
* @param {Signature} author
|
753
|
+
* @param {Signature} committer
|
754
|
+
* @param {String} message
|
755
|
+
* @return {Oid} The oid of the new commit
|
756
|
+
*/
|
757
|
+
Repository.prototype.createCommitOnHead = function(
|
758
|
+
filesToAdd,
|
759
|
+
author,
|
760
|
+
committer,
|
761
|
+
message) {
|
762
|
+
|
763
|
+
var repo = this;
|
764
|
+
|
765
|
+
return repo.refreshIndex()
|
766
|
+
.then(function(index) {
|
767
|
+
if (!filesToAdd) {
|
768
|
+
filesToAdd = [];
|
769
|
+
}
|
770
|
+
|
771
|
+
return filesToAdd
|
772
|
+
.reduce(function(lastFilePromise, filePath) {
|
773
|
+
return lastFilePromise
|
774
|
+
.then(function() {
|
775
|
+
return index.addByPath(filePath);
|
776
|
+
});
|
777
|
+
}, Promise.resolve())
|
778
|
+
.then(function() {
|
779
|
+
return index.write();
|
780
|
+
})
|
781
|
+
.then(function() {
|
782
|
+
return index.writeTree();
|
783
|
+
});
|
784
|
+
})
|
785
|
+
.then(function(treeOid) {
|
786
|
+
return repo.getHeadCommit()
|
787
|
+
.then(function(parent) {
|
788
|
+
if (parent !== null) { // To handle a fresh repo with no commits
|
789
|
+
parent = [parent];
|
790
|
+
}
|
791
|
+
return repo.createCommit(
|
792
|
+
"HEAD",
|
793
|
+
author,
|
794
|
+
committer,
|
795
|
+
message,
|
796
|
+
treeOid,
|
797
|
+
parent
|
798
|
+
);
|
799
|
+
});
|
800
|
+
});
|
801
|
+
};
|
802
|
+
|
803
|
+
/**
|
804
|
+
* Creates a new lightweight tag
|
805
|
+
*
|
806
|
+
* @async
|
807
|
+
* @param {String|Oid} String sha or Oid
|
808
|
+
* @param {String} name the name of the tag
|
809
|
+
* @return {Reference}
|
810
|
+
*/
|
811
|
+
Repository.prototype.createLightweightTag = function(oid, name) {
|
812
|
+
var repository = this;
|
813
|
+
|
814
|
+
return Commit.lookup(repository, oid)
|
815
|
+
.then(function(commit) {
|
816
|
+
// Final argument is `force` which overwrites any previous tag
|
817
|
+
return Tag.createLightweight(repository, name, commit, 0);
|
818
|
+
})
|
819
|
+
.then(function() {
|
820
|
+
return Reference.lookup(repository, "refs/tags/" + name);
|
821
|
+
});
|
822
|
+
};
|
823
|
+
|
824
|
+
/**
|
825
|
+
* Instantiate a new revision walker for browsing the Repository"s history.
|
826
|
+
* See also `Commit.prototype.history()`
|
827
|
+
*
|
828
|
+
* @return {Revwalk}
|
829
|
+
*/
|
830
|
+
Repository.prototype.createRevWalk = function() {
|
831
|
+
return Revwalk.create(this);
|
832
|
+
};
|
833
|
+
|
834
|
+
/**
|
835
|
+
* Creates a new annotated tag
|
836
|
+
*
|
837
|
+
* @async
|
838
|
+
* @param {String|Oid} String sha or Oid
|
839
|
+
* @param {String} name the name of the tag
|
840
|
+
* @param {String} message the description that will be attached to the
|
841
|
+
* annotated tag
|
842
|
+
* @return {Tag}
|
843
|
+
*/
|
844
|
+
Repository.prototype.createTag = function(oid, name, message) {
|
845
|
+
const repository = this;
|
846
|
+
let signature = null;
|
847
|
+
|
848
|
+
return repository.defaultSignature()
|
849
|
+
.then((signatureResult) => {
|
850
|
+
signature = signatureResult;
|
851
|
+
return Commit.lookup(repository, oid);
|
852
|
+
})
|
853
|
+
.then((commit) => {
|
854
|
+
// Final argument is `force` which overwrites any previous tag
|
855
|
+
return Tag.create(repository, name, commit, signature, message, 0);
|
856
|
+
})
|
857
|
+
.then((tagOid) => {
|
858
|
+
return repository.getTag(tagOid);
|
859
|
+
});
|
860
|
+
};
|
861
|
+
|
862
|
+
/**
|
863
|
+
* Gets the default signature for the default user and now timestamp
|
864
|
+
*
|
865
|
+
* @async
|
866
|
+
* @return {Signature}
|
867
|
+
*/
|
868
|
+
Repository.prototype.defaultSignature = function() {
|
869
|
+
return NodeGit.Signature.default(this)
|
870
|
+
.then((result) => {
|
871
|
+
if (!result || !result.name()) {
|
872
|
+
result = NodeGit.Signature.now("unknown", "unknown@example.com");
|
873
|
+
}
|
874
|
+
return result;
|
875
|
+
})
|
876
|
+
.catch(() => {
|
877
|
+
return NodeGit.Signature.now("unknown", "unknown@example.com");
|
878
|
+
});
|
879
|
+
};
|
880
|
+
|
881
|
+
/**
|
882
|
+
* Deletes a tag from a repository by the tag name.
|
883
|
+
*
|
884
|
+
* @async
|
885
|
+
* @param {String} Short or full tag name
|
886
|
+
*/
|
887
|
+
Repository.prototype.deleteTagByName = function(name) {
|
888
|
+
var repository = this;
|
889
|
+
|
890
|
+
name = ~name.indexOf("refs/tags/") ? name.substr(10) : name;
|
891
|
+
|
892
|
+
return Tag.delete(repository, name);
|
893
|
+
};
|
894
|
+
|
895
|
+
/**
|
896
|
+
* Discard line selection of a specified file.
|
897
|
+
* Assumes selected lines are unstaged.
|
898
|
+
*
|
899
|
+
* @async
|
900
|
+
* @param {String} filePath The relative path of this file in the repo
|
901
|
+
* @param {Array} selectedLines The array of DiffLine objects
|
902
|
+
* selected for discarding
|
903
|
+
* @return {Number} 0 or an error code
|
904
|
+
*/
|
905
|
+
Repository.prototype.discardLines =
|
906
|
+
function(filePath, selectedLines, additionalDiffOptions) {
|
907
|
+
var repo = this;
|
908
|
+
var fullFilePath = path.join(repo.workdir(), filePath);
|
909
|
+
var index;
|
910
|
+
var originalContent;
|
911
|
+
var filterList;
|
912
|
+
|
913
|
+
return repo.refreshIndex()
|
914
|
+
.then(function(indexResult) {
|
915
|
+
index = indexResult;
|
916
|
+
return FilterList.load(
|
917
|
+
repo,
|
918
|
+
null,
|
919
|
+
filePath,
|
920
|
+
Filter.MODE.CLEAN,
|
921
|
+
Filter.FLAG.DEFAULT
|
922
|
+
);
|
923
|
+
})
|
924
|
+
.then(function(_filterList) {
|
925
|
+
filterList = _filterList;
|
926
|
+
|
927
|
+
if (filterList) {
|
928
|
+
return filterList.applyToFile(repo, filePath);
|
929
|
+
}
|
930
|
+
|
931
|
+
return fse.readFile(fullFilePath, "utf8");
|
932
|
+
})
|
933
|
+
.then(function(content) {
|
934
|
+
originalContent = content;
|
935
|
+
return getPathHunks(repo, index, filePath, false, additionalDiffOptions);
|
936
|
+
})
|
937
|
+
.then(function(hunks) {
|
938
|
+
return applySelectedLinesToTarget(
|
939
|
+
originalContent, selectedLines, hunks, false, true
|
940
|
+
);
|
941
|
+
})
|
942
|
+
.then(function(newContent) {
|
943
|
+
return FilterList.load(
|
944
|
+
repo,
|
945
|
+
null,
|
946
|
+
filePath,
|
947
|
+
Filter.MODE.SMUDGE,
|
948
|
+
Filter.FLAG.DEFAULT
|
949
|
+
)
|
950
|
+
.then(function(_filterList) {
|
951
|
+
filterList = _filterList;
|
952
|
+
if (filterList) {
|
953
|
+
/* jshint ignore:start */
|
954
|
+
// We need the constructor for the check in NodeGit's C++ layer
|
955
|
+
// to accept an object, and this seems to be a nice way to do it
|
956
|
+
return filterList.applyToData(new String(newContent));
|
957
|
+
/* jshint ignore:end */
|
958
|
+
}
|
959
|
+
|
960
|
+
return newContent;
|
961
|
+
});
|
962
|
+
})
|
963
|
+
.then(function(filteredContent) {
|
964
|
+
return fse.writeFile(fullFilePath, filteredContent);
|
965
|
+
});
|
966
|
+
};
|
967
|
+
|
968
|
+
/**
|
969
|
+
* Fetches from a remote
|
970
|
+
*
|
971
|
+
* @async
|
972
|
+
* @param {String|Remote} remote
|
973
|
+
* @param {Object|FetchOptions} fetchOptions Options for the fetch, includes
|
974
|
+
* callbacks for fetching
|
975
|
+
*/
|
976
|
+
Repository.prototype.fetch = function(
|
977
|
+
remote,
|
978
|
+
fetchOptions)
|
979
|
+
{
|
980
|
+
var repo = this;
|
981
|
+
|
982
|
+
return repo.getRemote(remote)
|
983
|
+
.then(function(remote) {
|
984
|
+
return remote.fetch(null, fetchOptions, "Fetch from " + remote)
|
985
|
+
.then(function() {
|
986
|
+
return remote.disconnect();
|
987
|
+
});
|
988
|
+
});
|
989
|
+
};
|
990
|
+
|
991
|
+
/**
|
992
|
+
* Fetches from all remotes. This is done in series due to deadlocking issues
|
993
|
+
* with fetching from many remotes that can happen.
|
994
|
+
*
|
995
|
+
* @async
|
996
|
+
* @param {Object|FetchOptions} fetchOptions Options for the fetch, includes
|
997
|
+
* callbacks for fetching
|
998
|
+
*/
|
999
|
+
Repository.prototype.fetchAll = function(fetchOptions) {
|
1000
|
+
var repo = this;
|
1001
|
+
|
1002
|
+
function createCallbackWrapper(fn, remote) {
|
1003
|
+
return function() {
|
1004
|
+
var args = Array.prototype.slice.call(arguments);
|
1005
|
+
args.push(remote);
|
1006
|
+
|
1007
|
+
return fn.apply(this, args);
|
1008
|
+
}.bind(this);
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
fetchOptions = fetchOptions || {};
|
1012
|
+
|
1013
|
+
var remoteCallbacks = fetchOptions.callbacks || {};
|
1014
|
+
|
1015
|
+
var credentials = remoteCallbacks.credentials;
|
1016
|
+
var certificateCheck = remoteCallbacks.certificateCheck;
|
1017
|
+
var transferProgress = remoteCallbacks.transferProgress;
|
1018
|
+
|
1019
|
+
return repo.getRemoteNames()
|
1020
|
+
.then(function(remotes) {
|
1021
|
+
return remotes.reduce(function(fetchPromise, remote) {
|
1022
|
+
var wrappedFetchOptions = shallowClone(fetchOptions);
|
1023
|
+
var wrappedRemoteCallbacks = shallowClone(remoteCallbacks);
|
1024
|
+
|
1025
|
+
if (credentials) {
|
1026
|
+
wrappedRemoteCallbacks.credentials =
|
1027
|
+
createCallbackWrapper(credentials, remote);
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
if (certificateCheck) {
|
1031
|
+
wrappedRemoteCallbacks.certificateCheck =
|
1032
|
+
createCallbackWrapper(certificateCheck, remote);
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
if (transferProgress) {
|
1036
|
+
wrappedRemoteCallbacks.transferProgress =
|
1037
|
+
createCallbackWrapper(transferProgress, remote);
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
wrappedFetchOptions.callbacks = wrappedRemoteCallbacks;
|
1041
|
+
|
1042
|
+
return fetchPromise.then(function() {
|
1043
|
+
return repo.fetch(remote, wrappedFetchOptions);
|
1044
|
+
});
|
1045
|
+
}, Promise.resolve());
|
1046
|
+
});
|
1047
|
+
};
|
1048
|
+
|
1049
|
+
/**
|
1050
|
+
* @async
|
1051
|
+
* @param {FetchheadForeachCb} callback The callback function to be called on
|
1052
|
+
* each entry
|
1053
|
+
*/
|
1054
|
+
Repository.prototype.fetchheadForeach = function(callback) {
|
1055
|
+
return _fetchheadForeach.call(this, callback, null);
|
1056
|
+
};
|
1057
|
+
|
1058
|
+
/**
|
1059
|
+
* Retrieve the blob represented by the oid.
|
1060
|
+
*
|
1061
|
+
* @async
|
1062
|
+
* @param {String|Oid} String sha or Oid
|
1063
|
+
* @return {Blob}
|
1064
|
+
*/
|
1065
|
+
Repository.prototype.getBlob = function(oid) {
|
1066
|
+
var repository = this;
|
1067
|
+
|
1068
|
+
return Blob.lookup(repository, oid).then(function(blob) {
|
1069
|
+
blob.repo = repository;
|
1070
|
+
return blob;
|
1071
|
+
});
|
1072
|
+
};
|
1073
|
+
|
1074
|
+
/**
|
1075
|
+
* Look up a branch. Alias for `getReference`
|
1076
|
+
*
|
1077
|
+
* @async
|
1078
|
+
* @param {String|Reference} name Ref name, e.g. "master", "refs/heads/master"
|
1079
|
+
* or Branch Ref
|
1080
|
+
* @return {Reference}
|
1081
|
+
*/
|
1082
|
+
Repository.prototype.getBranch = function(name) {
|
1083
|
+
return this.getReference(name);
|
1084
|
+
};
|
1085
|
+
|
1086
|
+
/**
|
1087
|
+
* Look up a branch's most recent commit. Alias to `getReferenceCommit`
|
1088
|
+
*
|
1089
|
+
* @async
|
1090
|
+
* @param {String|Reference} name Ref name, e.g. "master", "refs/heads/master"
|
1091
|
+
* or Branch Ref
|
1092
|
+
* @return {Commit}
|
1093
|
+
*/
|
1094
|
+
Repository.prototype.getBranchCommit = function(name) {
|
1095
|
+
return this.getReferenceCommit(name);
|
1096
|
+
};
|
1097
|
+
|
1098
|
+
/**
|
1099
|
+
* Retrieve the commit identified by oid.
|
1100
|
+
*
|
1101
|
+
* @async
|
1102
|
+
* @param {String|Oid} String sha or Oid
|
1103
|
+
* @return {Commit}
|
1104
|
+
*/
|
1105
|
+
Repository.prototype.getCommit = function(oid) {
|
1106
|
+
var repository = this;
|
1107
|
+
|
1108
|
+
return Commit.lookup(repository, oid);
|
1109
|
+
};
|
1110
|
+
|
1111
|
+
/**
|
1112
|
+
* Gets the branch that HEAD currently points to
|
1113
|
+
* Is an alias to head()
|
1114
|
+
*
|
1115
|
+
* @async
|
1116
|
+
* @return {Reference}
|
1117
|
+
*/
|
1118
|
+
Repository.prototype.getCurrentBranch = function() {
|
1119
|
+
return this.head();
|
1120
|
+
};
|
1121
|
+
|
1122
|
+
/**
|
1123
|
+
* Retrieve the commit that HEAD is currently pointing to
|
1124
|
+
*
|
1125
|
+
* @async
|
1126
|
+
* @return {Commit}
|
1127
|
+
*/
|
1128
|
+
Repository.prototype.getHeadCommit = function() {
|
1129
|
+
var repo = this;
|
1130
|
+
|
1131
|
+
return Reference.nameToId(repo, "HEAD")
|
1132
|
+
.then(function(head) {
|
1133
|
+
return repo.getCommit(head);
|
1134
|
+
})
|
1135
|
+
.catch(function() {
|
1136
|
+
return null;
|
1137
|
+
});
|
1138
|
+
};
|
1139
|
+
|
1140
|
+
/**
|
1141
|
+
* Retrieve the master branch commit.
|
1142
|
+
*
|
1143
|
+
* @async
|
1144
|
+
* @return {Commit}
|
1145
|
+
*/
|
1146
|
+
Repository.prototype.getMasterCommit = function() {
|
1147
|
+
return this.getBranchCommit("master");
|
1148
|
+
};
|
1149
|
+
|
1150
|
+
/**
|
1151
|
+
* Lookup the reference with the given name.
|
1152
|
+
*
|
1153
|
+
* @async
|
1154
|
+
* @param {String|Reference} name Ref name, e.g. "master", "refs/heads/master"
|
1155
|
+
* or Branch Ref
|
1156
|
+
* @return {Reference}
|
1157
|
+
*/
|
1158
|
+
Repository.prototype.getReference = function(name) {
|
1159
|
+
var repository = this;
|
1160
|
+
|
1161
|
+
return Reference.dwim(this, name).then(function(reference) {
|
1162
|
+
if (reference.isSymbolic()) {
|
1163
|
+
return reference.resolve().then(function(reference) {
|
1164
|
+
reference.repo = repository;
|
1165
|
+
return reference;
|
1166
|
+
});
|
1167
|
+
}
|
1168
|
+
|
1169
|
+
reference.repo = repository;
|
1170
|
+
return reference;
|
1171
|
+
});
|
1172
|
+
};
|
1173
|
+
|
1174
|
+
/**
|
1175
|
+
* Look up a refs's commit.
|
1176
|
+
*
|
1177
|
+
* @async
|
1178
|
+
* @param {String|Reference} name Ref name, e.g. "master", "refs/heads/master"
|
1179
|
+
* or Branch Ref
|
1180
|
+
* @return {Commit}
|
1181
|
+
*/
|
1182
|
+
Repository.prototype.getReferenceCommit = function(name) {
|
1183
|
+
var repository = this;
|
1184
|
+
|
1185
|
+
return this.getReference(name).then(function(reference) {
|
1186
|
+
return repository.getCommit(reference.target());
|
1187
|
+
});
|
1188
|
+
};
|
1189
|
+
|
1190
|
+
/**
|
1191
|
+
* Lookup reference names for a repository.
|
1192
|
+
*
|
1193
|
+
* @async
|
1194
|
+
* @param {Reference.TYPE} type Type of reference to look up
|
1195
|
+
* @return {Array<String>}
|
1196
|
+
*/
|
1197
|
+
Repository.prototype.getReferenceNames = function(type) {
|
1198
|
+
return Repository.getReferences(this, type, true);
|
1199
|
+
};
|
1200
|
+
|
1201
|
+
/**
|
1202
|
+
* Lookup references for a repository.
|
1203
|
+
*
|
1204
|
+
* @async
|
1205
|
+
* @param {Reference.TYPE} type Type of reference to look up
|
1206
|
+
* @return {Array<Reference>}
|
1207
|
+
*/
|
1208
|
+
|
1209
|
+
/**
|
1210
|
+
* Gets a remote from the repo
|
1211
|
+
*
|
1212
|
+
* @async
|
1213
|
+
* @param {String|Remote} remote
|
1214
|
+
* @return {Remote} The remote object
|
1215
|
+
*/
|
1216
|
+
Repository.prototype.getRemote = function(remote) {
|
1217
|
+
if (remote instanceof NodeGit.Remote) {
|
1218
|
+
return Promise.resolve(remote);
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
return NodeGit.Remote.lookup(this, remote);
|
1222
|
+
};
|
1223
|
+
|
1224
|
+
/**
|
1225
|
+
* Lists out the remotes in the given repository.
|
1226
|
+
*
|
1227
|
+
* @async
|
1228
|
+
* @return {Object} Promise object.
|
1229
|
+
*/
|
1230
|
+
Repository.prototype.getRemoteNames = function() {
|
1231
|
+
return Remote.list(this);
|
1232
|
+
};
|
1233
|
+
|
1234
|
+
/**
|
1235
|
+
* Get the status of a repo to it's working directory
|
1236
|
+
*
|
1237
|
+
* @async
|
1238
|
+
* @param {obj} opts
|
1239
|
+
* @return {Array<StatusFile>}
|
1240
|
+
*/
|
1241
|
+
Repository.prototype.getStatus = function(opts) {
|
1242
|
+
var statuses = [];
|
1243
|
+
var statusCallback = function(path, status) {
|
1244
|
+
statuses.push(new StatusFile({path: path, status: status}));
|
1245
|
+
};
|
1246
|
+
|
1247
|
+
if (!opts) {
|
1248
|
+
opts = {
|
1249
|
+
flags: Status.OPT.INCLUDE_UNTRACKED |
|
1250
|
+
Status.OPT.RECURSE_UNTRACKED_DIRS
|
1251
|
+
};
|
1252
|
+
}
|
1253
|
+
|
1254
|
+
return Status.foreachExt(this, opts, statusCallback).then(function() {
|
1255
|
+
return statuses;
|
1256
|
+
});
|
1257
|
+
};
|
1258
|
+
|
1259
|
+
/**
|
1260
|
+
* Get extended statuses of a repo to it's working directory. Status entries
|
1261
|
+
* have `status`, `headToIndex` delta, and `indexToWorkdir` deltas
|
1262
|
+
*
|
1263
|
+
* @async
|
1264
|
+
* @param {obj} opts
|
1265
|
+
* @return {Array<StatusFile>}
|
1266
|
+
*/
|
1267
|
+
Repository.prototype.getStatusExt = function(opts) {
|
1268
|
+
var statuses = [];
|
1269
|
+
|
1270
|
+
if (!opts) {
|
1271
|
+
opts = {
|
1272
|
+
flags: Status.OPT.INCLUDE_UNTRACKED |
|
1273
|
+
Status.OPT.RECURSE_UNTRACKED_DIRS |
|
1274
|
+
Status.OPT.RENAMES_INDEX_TO_WORKDIR |
|
1275
|
+
Status.OPT.RENAMES_HEAD_TO_INDEX |
|
1276
|
+
Status.OPT.RENAMES_FROM_REWRITES
|
1277
|
+
};
|
1278
|
+
}
|
1279
|
+
|
1280
|
+
return StatusList.create(this, opts)
|
1281
|
+
.then(function(list) {
|
1282
|
+
for (var i = 0; i < list.entrycount(); i++) {
|
1283
|
+
var entry = Status.byIndex(list, i);
|
1284
|
+
statuses.push(new StatusFile({entry: entry}));
|
1285
|
+
}
|
1286
|
+
|
1287
|
+
return statuses;
|
1288
|
+
});
|
1289
|
+
};
|
1290
|
+
|
1291
|
+
/**
|
1292
|
+
* Get the names of the submodules in the repository.
|
1293
|
+
*
|
1294
|
+
* @async
|
1295
|
+
* @return {Array<String>}
|
1296
|
+
*/
|
1297
|
+
Repository.prototype.getSubmoduleNames = function() {
|
1298
|
+
var names = [];
|
1299
|
+
var submoduleCallback = function(submodule, name, payload) {
|
1300
|
+
names.push(name);
|
1301
|
+
};
|
1302
|
+
|
1303
|
+
return Submodule.foreach(this, submoduleCallback).then(function() {
|
1304
|
+
return names;
|
1305
|
+
});
|
1306
|
+
};
|
1307
|
+
|
1308
|
+
/**
|
1309
|
+
* Retrieve the tag represented by the oid.
|
1310
|
+
*
|
1311
|
+
* @async
|
1312
|
+
* @param {String|Oid} String sha or Oid
|
1313
|
+
* @return {Tag}
|
1314
|
+
*/
|
1315
|
+
Repository.prototype.getTag = function(oid) {
|
1316
|
+
var repository = this;
|
1317
|
+
|
1318
|
+
return Tag.lookup(repository, oid).then(function(reference) {
|
1319
|
+
reference.repo = repository;
|
1320
|
+
return reference;
|
1321
|
+
});
|
1322
|
+
};
|
1323
|
+
|
1324
|
+
/**
|
1325
|
+
* Retrieve the tag represented by the tag name.
|
1326
|
+
*
|
1327
|
+
* @async
|
1328
|
+
* @param {String} Short or full tag name
|
1329
|
+
* @return {Tag}
|
1330
|
+
*/
|
1331
|
+
Repository.prototype.getTagByName = function(name) {
|
1332
|
+
var repo = this;
|
1333
|
+
|
1334
|
+
name = ~name.indexOf("refs/tags/") ? name : "refs/tags/" + name;
|
1335
|
+
|
1336
|
+
return Reference.nameToId(repo, name)
|
1337
|
+
.then(function(oid) {
|
1338
|
+
return Tag.lookup(repo, oid);
|
1339
|
+
}).then(function(reference) {
|
1340
|
+
reference.repo = repo;
|
1341
|
+
return reference;
|
1342
|
+
});
|
1343
|
+
};
|
1344
|
+
|
1345
|
+
/**
|
1346
|
+
* Retrieve the tree represented by the oid.
|
1347
|
+
*
|
1348
|
+
* @async
|
1349
|
+
* @param {String|Oid} String sha or Oid
|
1350
|
+
* @return {Tree}
|
1351
|
+
*/
|
1352
|
+
Repository.prototype.getTree = function(oid) {
|
1353
|
+
var repository = this;
|
1354
|
+
|
1355
|
+
return Tree.lookup(repository, oid).then(function(tree) {
|
1356
|
+
tree.repo = repository;
|
1357
|
+
return tree;
|
1358
|
+
});
|
1359
|
+
};
|
1360
|
+
|
1361
|
+
/**
|
1362
|
+
* Returns true if the repository is in the APPLY_MAILBOX or
|
1363
|
+
* APPLY_MAILBOX_OR_REBASE state.
|
1364
|
+
* @return {Boolean}
|
1365
|
+
*/
|
1366
|
+
Repository.prototype.isApplyingMailbox = function() {
|
1367
|
+
var state = this.state();
|
1368
|
+
return state === NodeGit.Repository.STATE.APPLY_MAILBOX ||
|
1369
|
+
state === NodeGit.Repository.STATE.APPLY_MAILBOX_OR_REBASE;
|
1370
|
+
};
|
1371
|
+
|
1372
|
+
/**
|
1373
|
+
* Returns true if the repository is in the BISECT state.
|
1374
|
+
* @return {Boolean}
|
1375
|
+
*/
|
1376
|
+
Repository.prototype.isBisecting = function() {
|
1377
|
+
return this.state() === NodeGit.Repository.STATE.BISECT;
|
1378
|
+
};
|
1379
|
+
|
1380
|
+
/**
|
1381
|
+
* Returns true if the repository is in the CHERRYPICK state.
|
1382
|
+
* @return {Boolean}
|
1383
|
+
*/
|
1384
|
+
Repository.prototype.isCherrypicking = function() {
|
1385
|
+
return this.state() === NodeGit.Repository.STATE.CHERRYPICK;
|
1386
|
+
};
|
1387
|
+
|
1388
|
+
/**
|
1389
|
+
* Returns true if the repository is in the default NONE state.
|
1390
|
+
* @return {Boolean}
|
1391
|
+
*/
|
1392
|
+
Repository.prototype.isDefaultState = function() {
|
1393
|
+
return this.state() === NodeGit.Repository.STATE.NONE;
|
1394
|
+
};
|
1395
|
+
|
1396
|
+
/**
|
1397
|
+
* Returns true if the repository is in the MERGE state.
|
1398
|
+
* @return {Boolean}
|
1399
|
+
*/
|
1400
|
+
Repository.prototype.isMerging = function() {
|
1401
|
+
return this.state() === NodeGit.Repository.STATE.MERGE;
|
1402
|
+
};
|
1403
|
+
|
1404
|
+
/**
|
1405
|
+
* Returns true if the repository is in the REBASE, REBASE_INTERACTIVE, or
|
1406
|
+
* REBASE_MERGE state.
|
1407
|
+
* @return {Boolean}
|
1408
|
+
*/
|
1409
|
+
Repository.prototype.isRebasing = function() {
|
1410
|
+
var state = this.state();
|
1411
|
+
return state === NodeGit.Repository.STATE.REBASE ||
|
1412
|
+
state === NodeGit.Repository.STATE.REBASE_INTERACTIVE ||
|
1413
|
+
state === NodeGit.Repository.STATE.REBASE_MERGE;
|
1414
|
+
};
|
1415
|
+
|
1416
|
+
/**
|
1417
|
+
* Returns true if the repository is in the REVERT state.
|
1418
|
+
* @return {Boolean}
|
1419
|
+
*/
|
1420
|
+
Repository.prototype.isReverting = function() {
|
1421
|
+
return this.state() === NodeGit.Repository.STATE.REVERT;
|
1422
|
+
};
|
1423
|
+
|
1424
|
+
/**
|
1425
|
+
* Rebases a branch onto another branch
|
1426
|
+
*
|
1427
|
+
* @async
|
1428
|
+
* @param {String} branch
|
1429
|
+
* @param {String} upstream
|
1430
|
+
* @param {String} onto
|
1431
|
+
* @param {Signature} signature Identity of the one performing the rebase
|
1432
|
+
* @param {Function} beforeNextFn Callback to be called before each step
|
1433
|
+
* of the rebase. If the callback returns a
|
1434
|
+
* promise, the rebase will resume when the
|
1435
|
+
* promise resolves. The rebase object is
|
1436
|
+
* is passed to the callback.
|
1437
|
+
* @param {Function} beforeFinishFn Callback called before the invocation
|
1438
|
+
* of finish(). If the callback returns a
|
1439
|
+
* promise, finish() will be called when the
|
1440
|
+
* promise resolves. This callback will be
|
1441
|
+
* provided a detailed overview of the rebase
|
1442
|
+
* @param {RebaseOptions} rebaseOptions Options to initialize the rebase object
|
1443
|
+
* with
|
1444
|
+
* @return {Oid|Index} A commit id for a succesful merge or an index for a
|
1445
|
+
* rebase with conflicts
|
1446
|
+
*/
|
1447
|
+
Repository.prototype.rebaseBranches = function(
|
1448
|
+
branch,
|
1449
|
+
upstream,
|
1450
|
+
onto,
|
1451
|
+
signature,
|
1452
|
+
beforeNextFn,
|
1453
|
+
beforeFinishFn,
|
1454
|
+
rebaseOptions
|
1455
|
+
)
|
1456
|
+
{
|
1457
|
+
const repo = this;
|
1458
|
+
let branchCommit;
|
1459
|
+
let upstreamCommit;
|
1460
|
+
let ontoCommit;
|
1461
|
+
let mergeOptions = (rebaseOptions || {}).mergeOptions;
|
1462
|
+
|
1463
|
+
let promiseChain = Promise.resolve();
|
1464
|
+
|
1465
|
+
if (!signature) {
|
1466
|
+
promiseChain = promiseChain
|
1467
|
+
.then(() => repo.defaultSignature())
|
1468
|
+
.then((signatureResult) => {
|
1469
|
+
signature = signatureResult;
|
1470
|
+
});
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
return Promise.all([
|
1474
|
+
repo.getReference(branch),
|
1475
|
+
upstream ? repo.getReference(upstream) : null,
|
1476
|
+
onto ? repo.getReference(onto) : null
|
1477
|
+
])
|
1478
|
+
.then(function(refs) {
|
1479
|
+
return Promise.all([
|
1480
|
+
NodeGit.AnnotatedCommit.fromRef(repo, refs[0]),
|
1481
|
+
upstream ? NodeGit.AnnotatedCommit.fromRef(repo, refs[1]) : null,
|
1482
|
+
onto ? NodeGit.AnnotatedCommit.fromRef(repo, refs[2]) : null
|
1483
|
+
]);
|
1484
|
+
})
|
1485
|
+
.then(function(annotatedCommits) {
|
1486
|
+
branchCommit = annotatedCommits[0];
|
1487
|
+
upstreamCommit = annotatedCommits[1];
|
1488
|
+
ontoCommit = annotatedCommits[2];
|
1489
|
+
|
1490
|
+
return NodeGit.Merge.base(repo, branchCommit.id(), upstreamCommit.id());
|
1491
|
+
})
|
1492
|
+
.then(function(oid) {
|
1493
|
+
if (oid.toString() === branchCommit.id().toString()) {
|
1494
|
+
// we just need to fast-forward
|
1495
|
+
return repo.mergeBranches(branch, upstream, null, null, mergeOptions)
|
1496
|
+
.then(function() {
|
1497
|
+
// checkout 'branch' to match the behavior of rebase
|
1498
|
+
return repo.checkoutBranch(branch);
|
1499
|
+
});
|
1500
|
+
} else if (oid.toString() === upstreamCommit.id().toString()) {
|
1501
|
+
// 'branch' is already on top of 'upstream'
|
1502
|
+
// checkout 'branch' to match the behavior of rebase
|
1503
|
+
return repo.checkoutBranch(branch);
|
1504
|
+
}
|
1505
|
+
|
1506
|
+
return NodeGit.Rebase.init(
|
1507
|
+
repo,
|
1508
|
+
branchCommit,
|
1509
|
+
upstreamCommit,
|
1510
|
+
ontoCommit,
|
1511
|
+
rebaseOptions
|
1512
|
+
)
|
1513
|
+
.then(function(rebase) {
|
1514
|
+
return performRebase(
|
1515
|
+
repo,
|
1516
|
+
rebase,
|
1517
|
+
signature,
|
1518
|
+
beforeNextFn,
|
1519
|
+
beforeFinishFn
|
1520
|
+
);
|
1521
|
+
})
|
1522
|
+
.then(function(error) {
|
1523
|
+
if (error) {
|
1524
|
+
throw error;
|
1525
|
+
}
|
1526
|
+
});
|
1527
|
+
})
|
1528
|
+
.then(function() {
|
1529
|
+
return repo.getBranchCommit("HEAD");
|
1530
|
+
});
|
1531
|
+
};
|
1532
|
+
|
1533
|
+
/**
|
1534
|
+
* Grabs a fresh copy of the index from the repository. Invalidates
|
1535
|
+
* all previously grabbed indexes
|
1536
|
+
*
|
1537
|
+
* @async
|
1538
|
+
* @return {Index}
|
1539
|
+
*/
|
1540
|
+
Repository.prototype.refreshIndex = function() {
|
1541
|
+
var repo = this;
|
1542
|
+
|
1543
|
+
repo.setIndex(); // clear the index
|
1544
|
+
|
1545
|
+
return repo.index();
|
1546
|
+
};
|
1547
|
+
|
1548
|
+
/**
|
1549
|
+
* Merge a branch onto another branch
|
1550
|
+
*
|
1551
|
+
* @async
|
1552
|
+
* @param {String|Reference} to
|
1553
|
+
* @param {String|Reference} from
|
1554
|
+
* @param {Signature} signature
|
1555
|
+
* @param {Merge.PREFERENCE} mergePreference
|
1556
|
+
* @param {MergeOptions} mergeOptions
|
1557
|
+
* @param {MergeBranchOptions} mergeBranchOptions
|
1558
|
+
* @return {Oid|Index} A commit id for a succesful merge or an index for a
|
1559
|
+
* merge with conflicts
|
1560
|
+
*/
|
1561
|
+
Repository.prototype.mergeBranches = function(
|
1562
|
+
to,
|
1563
|
+
from,
|
1564
|
+
signature,
|
1565
|
+
mergePreference,
|
1566
|
+
mergeOptions,
|
1567
|
+
mergeBranchOptions
|
1568
|
+
) {
|
1569
|
+
const repo = this;
|
1570
|
+
let fromBranch;
|
1571
|
+
let toBranch;
|
1572
|
+
// Support old parameter `processMergeMessageCallback`
|
1573
|
+
const isOldOptionParameter = typeof mergeBranchOptions === "function";
|
1574
|
+
if (isOldOptionParameter) {
|
1575
|
+
console.error("DeprecationWarning: Repository#mergeBranches parameter " +
|
1576
|
+
"processMergeMessageCallback, use MergeBranchOptions");
|
1577
|
+
}
|
1578
|
+
const processMergeMessageCallback = mergeBranchOptions &&
|
1579
|
+
(isOldOptionParameter ?
|
1580
|
+
mergeBranchOptions :
|
1581
|
+
mergeBranchOptions.processMergeMessageCallback) ||
|
1582
|
+
function (message) { return message; };
|
1583
|
+
const signingCallback = mergeBranchOptions && mergeBranchOptions.signingCb;
|
1584
|
+
|
1585
|
+
mergePreference = mergePreference || NodeGit.Merge.PREFERENCE.NONE;
|
1586
|
+
mergeOptions = normalizeOptions(mergeOptions, NodeGit.MergeOptions);
|
1587
|
+
|
1588
|
+
let promiseChain = Promise.resolve();
|
1589
|
+
|
1590
|
+
if (!signature) {
|
1591
|
+
promiseChain = promiseChain
|
1592
|
+
.then(() => repo.defaultSignature())
|
1593
|
+
.then((signatureResult) => {
|
1594
|
+
signature = signatureResult;
|
1595
|
+
});
|
1596
|
+
}
|
1597
|
+
|
1598
|
+
return promiseChain.then(() => Promise.all([
|
1599
|
+
repo.getBranch(to),
|
1600
|
+
repo.getBranch(from)
|
1601
|
+
]))
|
1602
|
+
.then((objects) => {
|
1603
|
+
toBranch = objects[0];
|
1604
|
+
fromBranch = objects[1];
|
1605
|
+
|
1606
|
+
return Promise.all([
|
1607
|
+
repo.getBranchCommit(toBranch),
|
1608
|
+
repo.getBranchCommit(fromBranch)
|
1609
|
+
]);
|
1610
|
+
})
|
1611
|
+
.then((branchCommits) => {
|
1612
|
+
var toCommitOid = branchCommits[0].toString();
|
1613
|
+
var fromCommitOid = branchCommits[1].toString();
|
1614
|
+
|
1615
|
+
return NodeGit.Merge.base(repo, toCommitOid, fromCommitOid)
|
1616
|
+
.then((baseCommit) => {
|
1617
|
+
if (baseCommit.toString() == fromCommitOid) {
|
1618
|
+
// The commit we're merging to is already in our history.
|
1619
|
+
// nothing to do so just return the commit the branch is on
|
1620
|
+
return toCommitOid;
|
1621
|
+
}
|
1622
|
+
else if (baseCommit.toString() == toCommitOid &&
|
1623
|
+
mergePreference !== NodeGit.Merge.PREFERENCE.NO_FASTFORWARD) {
|
1624
|
+
// fast forward
|
1625
|
+
var message =
|
1626
|
+
"Fast forward branch " +
|
1627
|
+
toBranch.shorthand() +
|
1628
|
+
" to branch " +
|
1629
|
+
fromBranch.shorthand();
|
1630
|
+
|
1631
|
+
return branchCommits[1].getTree()
|
1632
|
+
.then((tree) => {
|
1633
|
+
if (toBranch.isHead()) {
|
1634
|
+
// Checkout the tree if we're on the branch
|
1635
|
+
var opts = {
|
1636
|
+
checkoutStrategy: NodeGit.Checkout.STRATEGY.SAFE |
|
1637
|
+
NodeGit.Checkout.STRATEGY.RECREATE_MISSING
|
1638
|
+
};
|
1639
|
+
return NodeGit.Checkout.tree(repo, tree, opts);
|
1640
|
+
}
|
1641
|
+
})
|
1642
|
+
.then(() => {
|
1643
|
+
return toBranch.setTarget(
|
1644
|
+
fromCommitOid,
|
1645
|
+
message)
|
1646
|
+
.then(() => {
|
1647
|
+
return fromCommitOid;
|
1648
|
+
});
|
1649
|
+
});
|
1650
|
+
}
|
1651
|
+
else if (mergePreference !== NodeGit.Merge.PREFERENCE.FASTFORWARD_ONLY) {
|
1652
|
+
var updateHead;
|
1653
|
+
// We have to merge. Lets do it!
|
1654
|
+
return NodeGit.Reference.lookup(repo, "HEAD")
|
1655
|
+
.then((headRef) => {
|
1656
|
+
return headRef.resolve();
|
1657
|
+
})
|
1658
|
+
.then((headRef) => {
|
1659
|
+
updateHead = !!headRef && (headRef.name() === toBranch.name());
|
1660
|
+
return NodeGit.Merge.commits(
|
1661
|
+
repo,
|
1662
|
+
toCommitOid,
|
1663
|
+
fromCommitOid,
|
1664
|
+
mergeOptions
|
1665
|
+
);
|
1666
|
+
})
|
1667
|
+
.then((index) => {
|
1668
|
+
// if we have conflicts then throw the index
|
1669
|
+
if (index.hasConflicts()) {
|
1670
|
+
throw index;
|
1671
|
+
}
|
1672
|
+
|
1673
|
+
// No conflicts so just go ahead with the merge
|
1674
|
+
return index.writeTreeTo(repo);
|
1675
|
+
})
|
1676
|
+
.then((oid) => {
|
1677
|
+
var mergeDecorator;
|
1678
|
+
if (fromBranch.isTag()) {
|
1679
|
+
mergeDecorator = "tag";
|
1680
|
+
} else if (fromBranch.isRemote()) {
|
1681
|
+
mergeDecorator = "remote-tracking branch";
|
1682
|
+
} else {
|
1683
|
+
mergeDecorator = "branch";
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
var message =
|
1687
|
+
"Merge " +
|
1688
|
+
mergeDecorator +
|
1689
|
+
" '" +
|
1690
|
+
fromBranch.shorthand() +
|
1691
|
+
"'";
|
1692
|
+
|
1693
|
+
// https://github.com/git/git/blob/master/builtin/fmt-merge-msg.c#L456-L459
|
1694
|
+
if (toBranch.shorthand() !== "master") {
|
1695
|
+
message += " into " + toBranch.shorthand();
|
1696
|
+
}
|
1697
|
+
|
1698
|
+
return Promise.all([oid, processMergeMessageCallback(message)]);
|
1699
|
+
})
|
1700
|
+
.then(([oid, message]) => {
|
1701
|
+
if (signingCallback) {
|
1702
|
+
return repo.createCommitWithSignature(
|
1703
|
+
toBranch.name(),
|
1704
|
+
signature,
|
1705
|
+
signature,
|
1706
|
+
message,
|
1707
|
+
oid,
|
1708
|
+
[toCommitOid, fromCommitOid],
|
1709
|
+
signingCallback
|
1710
|
+
);
|
1711
|
+
}
|
1712
|
+
return repo.createCommit(
|
1713
|
+
toBranch.name(),
|
1714
|
+
signature,
|
1715
|
+
signature,
|
1716
|
+
message,
|
1717
|
+
oid,
|
1718
|
+
[toCommitOid, fromCommitOid]
|
1719
|
+
);
|
1720
|
+
})
|
1721
|
+
.then((commit) => {
|
1722
|
+
// we've updated the checked out branch, so make sure we update
|
1723
|
+
// head so that our index isn't messed up
|
1724
|
+
if (updateHead) {
|
1725
|
+
return repo.getBranch(to)
|
1726
|
+
.then((branch) => {
|
1727
|
+
return repo.getBranchCommit(branch);
|
1728
|
+
})
|
1729
|
+
.then((branchCommit) => {
|
1730
|
+
return branchCommit.getTree();
|
1731
|
+
})
|
1732
|
+
.then((tree) => {
|
1733
|
+
var opts = {
|
1734
|
+
checkoutStrategy: NodeGit.Checkout.STRATEGY.SAFE |
|
1735
|
+
NodeGit.Checkout.STRATEGY.RECREATE_MISSING
|
1736
|
+
};
|
1737
|
+
return NodeGit.Checkout.tree(repo, tree, opts);
|
1738
|
+
})
|
1739
|
+
.then(() => {
|
1740
|
+
return commit;
|
1741
|
+
});
|
1742
|
+
}
|
1743
|
+
else {
|
1744
|
+
return commit;
|
1745
|
+
}
|
1746
|
+
});
|
1747
|
+
}
|
1748
|
+
else {
|
1749
|
+
// A non fast-forwardable merge with ff-only
|
1750
|
+
return toCommitOid;
|
1751
|
+
}
|
1752
|
+
});
|
1753
|
+
});
|
1754
|
+
};
|
1755
|
+
|
1756
|
+
/**
|
1757
|
+
* @async
|
1758
|
+
* @param {MergeheadForeachCb} callback The callback function to be called on
|
1759
|
+
* each entry
|
1760
|
+
*/
|
1761
|
+
Repository.prototype.mergeheadForeach = function(callback) {
|
1762
|
+
return _mergeheadForeach.call(this, callback, null);
|
1763
|
+
};
|
1764
|
+
|
1765
|
+
/**
|
1766
|
+
* Stages or unstages line selection of a specified file
|
1767
|
+
*
|
1768
|
+
* @async
|
1769
|
+
* @param {String|Array} filePath The relative path of this file in the repo
|
1770
|
+
* @param {Boolean} stageNew Set to stage new filemode. Unset to unstage.
|
1771
|
+
* @return {Number} 0 or an error code
|
1772
|
+
*/
|
1773
|
+
Repository.prototype.stageFilemode =
|
1774
|
+
function(filePath, stageNew, additionalDiffOptions) {
|
1775
|
+
var repo = this;
|
1776
|
+
var index;
|
1777
|
+
var diffOptions = additionalDiffOptions ? {
|
1778
|
+
flags: additionalDiffOptions
|
1779
|
+
} : undefined;
|
1780
|
+
var diffPromise = stageNew ?
|
1781
|
+
NodeGit.Diff.indexToWorkdir(repo, index, {
|
1782
|
+
flags:
|
1783
|
+
NodeGit.Diff.OPTION.SHOW_UNTRACKED_CONTENT |
|
1784
|
+
NodeGit.Diff.OPTION.RECURSE_UNTRACKED_DIRS |
|
1785
|
+
(additionalDiffOptions || 0)
|
1786
|
+
})
|
1787
|
+
:
|
1788
|
+
repo.getHeadCommit()
|
1789
|
+
.then(function getTreeFromCommit(commit) {
|
1790
|
+
return commit.getTree();
|
1791
|
+
})
|
1792
|
+
.then(function getDiffFromTree(tree) {
|
1793
|
+
return NodeGit.Diff.treeToIndex(repo, tree, index, diffOptions);
|
1794
|
+
});
|
1795
|
+
var filePaths = (filePath instanceof Array) ? filePath : [filePath];
|
1796
|
+
|
1797
|
+
var indexLock = repo.path().replace(".git/", "") + ".git/index.lock";
|
1798
|
+
|
1799
|
+
return fse.remove(indexLock)
|
1800
|
+
.then(function() {
|
1801
|
+
return repo.refreshIndex();
|
1802
|
+
})
|
1803
|
+
.then(function(indexResult) {
|
1804
|
+
index = indexResult;
|
1805
|
+
})
|
1806
|
+
.then(function() {
|
1807
|
+
return diffPromise;
|
1808
|
+
})
|
1809
|
+
.then(function(diff) {
|
1810
|
+
var origLength = filePaths.length;
|
1811
|
+
var fileFilterPromises = fp.map(function(p) {
|
1812
|
+
return NodeGit.Status.file(repo, p)
|
1813
|
+
.then(function(status) {
|
1814
|
+
return {
|
1815
|
+
path: p,
|
1816
|
+
filter: (
|
1817
|
+
(status & NodeGit.Status.STATUS.WT_MODIFIED) ||
|
1818
|
+
(status & NodeGit.Status.STATUS.INDEX_MODIFIED)
|
1819
|
+
)
|
1820
|
+
};
|
1821
|
+
});
|
1822
|
+
}, filePaths);
|
1823
|
+
|
1824
|
+
return Promise.all(fileFilterPromises)
|
1825
|
+
.then(function(results) {
|
1826
|
+
filePaths = fp.flow([
|
1827
|
+
fp.filter(function(filterResult) {
|
1828
|
+
return filterResult.filter;
|
1829
|
+
}),
|
1830
|
+
fp.map(function(filterResult) {
|
1831
|
+
return filterResult.path;
|
1832
|
+
})
|
1833
|
+
])(results);
|
1834
|
+
|
1835
|
+
if (filePaths.length === 0 && origLength > 0) {
|
1836
|
+
return Promise.reject
|
1837
|
+
("Selected staging is only available on modified files.");
|
1838
|
+
}
|
1839
|
+
return diff.patches();
|
1840
|
+
});
|
1841
|
+
})
|
1842
|
+
.then(function(patches) {
|
1843
|
+
var pathPatches = patches.filter(function(patch) {
|
1844
|
+
return ~filePaths.indexOf(patch.newFile().path());
|
1845
|
+
});
|
1846
|
+
if (pathPatches.length === 0) {
|
1847
|
+
return Promise.reject("No differences found for this file.");
|
1848
|
+
}
|
1849
|
+
|
1850
|
+
return pathPatches
|
1851
|
+
.reduce(function(lastIndexAddPromise, pathPatch) {
|
1852
|
+
var entry = index.getByPath(pathPatch.newFile().path(), 0);
|
1853
|
+
|
1854
|
+
entry.mode = stageNew ?
|
1855
|
+
pathPatch.newFile().mode() : pathPatch.oldFile().mode();
|
1856
|
+
|
1857
|
+
return lastIndexAddPromise
|
1858
|
+
.then(function() {
|
1859
|
+
return index.add(entry);
|
1860
|
+
});
|
1861
|
+
}, Promise.resolve());
|
1862
|
+
})
|
1863
|
+
.then(function() {
|
1864
|
+
return index.write();
|
1865
|
+
});
|
1866
|
+
};
|
1867
|
+
|
1868
|
+
/**
|
1869
|
+
* Stages or unstages line selection of a specified file
|
1870
|
+
*
|
1871
|
+
* @async
|
1872
|
+
* @param {String} filePath The relative path of this file in the repo
|
1873
|
+
* @param {Array} selectedLines The array of DiffLine objects
|
1874
|
+
* selected for staging or unstaging
|
1875
|
+
* @param {Boolean} isStaged Are the selected lines currently staged
|
1876
|
+
* @return {Number} 0 or an error code
|
1877
|
+
*/
|
1878
|
+
Repository.prototype.stageLines =
|
1879
|
+
function(filePath, selectedLines, isSelectionStaged, additionalDiffOptions) {
|
1880
|
+
|
1881
|
+
var repo = this;
|
1882
|
+
var index;
|
1883
|
+
var originalBlob;
|
1884
|
+
|
1885
|
+
// The following chain checks if there is a patch with no hunks left for the
|
1886
|
+
// file, and no filemode changes were done on the file. It is then safe to
|
1887
|
+
// stage the entire file so the file doesn't show as having unstaged changes
|
1888
|
+
// in `git status`. Also, check if there are no type changes.
|
1889
|
+
var lastHunkStagedPromise = function lastHunkStagedPromise(result) {
|
1890
|
+
return NodeGit.Diff.indexToWorkdir(repo, index, {
|
1891
|
+
flags:
|
1892
|
+
NodeGit.Diff.OPTION.SHOW_UNTRACKED_CONTENT |
|
1893
|
+
NodeGit.Diff.OPTION.RECURSE_UNTRACKED_DIRS |
|
1894
|
+
(additionalDiffOptions || 0)
|
1895
|
+
})
|
1896
|
+
.then(function(diff) {
|
1897
|
+
return diff.patches();
|
1898
|
+
})
|
1899
|
+
.then(function(patches) {
|
1900
|
+
var pathPatch = patches.filter(function(patch) {
|
1901
|
+
return patch.newFile().path() === filePath;
|
1902
|
+
});
|
1903
|
+
var emptyPatch = false;
|
1904
|
+
if (pathPatch.length > 0) {
|
1905
|
+
// No hunks, unchanged file mode, and no type changes.
|
1906
|
+
emptyPatch = pathPatch[0].size() === 0 &&
|
1907
|
+
pathPatch[0].oldFile().mode() === pathPatch[0].newFile().mode() &&
|
1908
|
+
!pathPatch[0].isTypeChange();
|
1909
|
+
}
|
1910
|
+
if (emptyPatch) {
|
1911
|
+
return index.addByPath(filePath)
|
1912
|
+
.then(function() {
|
1913
|
+
return index.write();
|
1914
|
+
});
|
1915
|
+
}
|
1916
|
+
|
1917
|
+
return result;
|
1918
|
+
});
|
1919
|
+
};
|
1920
|
+
|
1921
|
+
return repo.refreshIndex()
|
1922
|
+
.then(function(indexResult) {
|
1923
|
+
index = indexResult;
|
1924
|
+
var pathOid = index.getByPath(filePath).id;
|
1925
|
+
|
1926
|
+
return repo.getBlob(pathOid);
|
1927
|
+
})
|
1928
|
+
.then(function(blob) {
|
1929
|
+
originalBlob = blob;
|
1930
|
+
|
1931
|
+
return getPathHunks(
|
1932
|
+
repo,
|
1933
|
+
index,
|
1934
|
+
filePath,
|
1935
|
+
isSelectionStaged,
|
1936
|
+
additionalDiffOptions
|
1937
|
+
);
|
1938
|
+
})
|
1939
|
+
.then(function(hunks) {
|
1940
|
+
return applySelectedLinesToTarget(
|
1941
|
+
originalBlob, selectedLines, hunks, isSelectionStaged
|
1942
|
+
);
|
1943
|
+
})
|
1944
|
+
.then(function(newContent) {
|
1945
|
+
var newContentBuffer = Buffer.from(newContent);
|
1946
|
+
|
1947
|
+
return repo.createBlobFromBuffer(newContentBuffer);
|
1948
|
+
})
|
1949
|
+
.then(newOid => repo.getBlob(newOid))
|
1950
|
+
.then(function(newBlob) {
|
1951
|
+
var entry = index.getByPath(filePath, 0);
|
1952
|
+
entry.id = newBlob.id();
|
1953
|
+
entry.path = filePath;
|
1954
|
+
entry.fileSize = newBlob.content().length;
|
1955
|
+
|
1956
|
+
return index.add(entry);
|
1957
|
+
})
|
1958
|
+
.then(function() {
|
1959
|
+
return index.write();
|
1960
|
+
})
|
1961
|
+
.then(function(result) {
|
1962
|
+
if (isSelectionStaged) {
|
1963
|
+
return result;
|
1964
|
+
}
|
1965
|
+
|
1966
|
+
return lastHunkStagedPromise(result);
|
1967
|
+
});
|
1968
|
+
};
|
1969
|
+
|
1970
|
+
/**
|
1971
|
+
* Create a new tree builder.
|
1972
|
+
*
|
1973
|
+
* @param {Tree} tree
|
1974
|
+
*/
|
1975
|
+
Repository.prototype.treeBuilder = function() {
|
1976
|
+
var builder = TreeBuilder.create(null);
|
1977
|
+
|
1978
|
+
builder.root = builder;
|
1979
|
+
builder.repo = this;
|
1980
|
+
|
1981
|
+
return builder;
|
1982
|
+
};
|