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.
Files changed (49) hide show
  1. package/lib/README.md +3 -0
  2. package/lib/attr.js +20 -0
  3. package/lib/blame.js +20 -0
  4. package/lib/blob.js +73 -0
  5. package/lib/branch.js +19 -0
  6. package/lib/buf.js +11 -0
  7. package/lib/checkout.js +51 -0
  8. package/lib/cherrypick.js +73 -0
  9. package/lib/clone.js +33 -0
  10. package/lib/commit.js +437 -0
  11. package/lib/config.js +25 -0
  12. package/lib/convenient_hunks.js +61 -0
  13. package/lib/convenient_patch.js +131 -0
  14. package/lib/credential.js +33 -0
  15. package/lib/diff.js +113 -0
  16. package/lib/diff_file.js +38 -0
  17. package/lib/diff_line.js +32 -0
  18. package/lib/enums.js +689 -0
  19. package/lib/error.js +17 -0
  20. package/lib/filter_registry.js +25 -0
  21. package/lib/index.js +103 -0
  22. package/lib/libgit2.js +6 -0
  23. package/lib/merge.js +72 -0
  24. package/lib/nodegit.js +1333 -0
  25. package/lib/note.js +17 -0
  26. package/lib/object.js +45 -0
  27. package/lib/odb_object.js +9 -0
  28. package/lib/oid.js +23 -0
  29. package/lib/rebase.js +142 -0
  30. package/lib/reference.js +213 -0
  31. package/lib/remote.js +270 -0
  32. package/lib/repository.js +1982 -0
  33. package/lib/reset.js +76 -0
  34. package/lib/revert.js +77 -0
  35. package/lib/revwalk.js +142 -0
  36. package/lib/signature.js +38 -0
  37. package/lib/stash.js +62 -0
  38. package/lib/status.js +18 -0
  39. package/lib/status_file.js +106 -0
  40. package/lib/status_list.js +12 -0
  41. package/lib/submodule.js +51 -0
  42. package/lib/tag.js +135 -0
  43. package/lib/tree.js +175 -0
  44. package/lib/tree_entry.js +99 -0
  45. package/lib/utils/lookup_wrapper.js +39 -0
  46. package/lib/utils/normalize_fetch_options.js +43 -0
  47. package/lib/utils/normalize_options.js +29 -0
  48. package/lib/utils/shallow_clone.js +14 -0
  49. 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
+ };