@swimlane/nodegit 1.1.2

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