pj-nodegit 0.18.4 → 0.18.5

Sign up to get free protection for your applications and to get access to all the features.
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
+ };