isomorphic-git 1.17.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -345,6 +345,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
345
345
  </tr>
346
346
  <tr>
347
347
  <td align="center"><a href="https://github.com/seanpoulter"><img src="https://avatars.githubusercontent.com/u/2585460?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sean Poulter</b></sub></a><br /><a href="#maintenance-seanpoulter" title="Maintenance">🚧</a></td>
348
+ <td align="center"><a href="https://github.com/araknast"><img src="https://avatars.githubusercontent.com/u/84164531?v=4?s=60" width="60px;" alt=""/><br /><sub><b>\\</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=araknast" title="Code">💻</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=araknast" title="Tests">⚠️</a></td>
348
349
  </tr>
349
350
  </table>
350
351
 
@@ -1,9 +1,8 @@
1
1
  [
2
- "X Chrome Mobile 99.0.4844 (Android 0.0.0)",
3
- "HeadlessChrome 0.0.0 (Linux 0.0.0)",
4
- "Firefox 99.0.0 (Ubuntu 0.0.0)",
5
- "Chrome 79.0.3945 (Windows 10 0.0.0)",
6
- "Safari 13.1.0 (Mac OS X 10.15.4)",
7
- "Mobile Safari 13.0.0 (iOS 13.0.0)",
8
- "Chrome Mobile 99.0.4844 (Android 0.0.0)"
2
+ "Chrome Headless 79.0.3945.0 (Linux x86_64)",
3
+ "Firefox 100.0 (Ubuntu 0.0.0)",
4
+ "Chrome 100.0.4896.127 (Android 10)",
5
+ "Edge 79.0.309.65 (Windows 10)",
6
+ "Mobile Safari 13.0 (iOS 13.0)",
7
+ "Safari 13.1 (Mac OS 10.15.4)"
9
8
  ]
package/index.cjs CHANGED
@@ -239,6 +239,19 @@ var diff3Merge = _interopDefault(require('diff3'));
239
239
  * @return {{signature: string} | Promise<{signature: string}>} - an 'ASCII armor' encoded "detached" signature
240
240
  */
241
241
 
242
+ /**
243
+ * @typedef {Object} MergeDriverParams
244
+ * @property {Array<string>} branches
245
+ * @property {Array<string>} contents
246
+ * @property {string} path
247
+ */
248
+
249
+ /**
250
+ * @callback MergeDriverCallback
251
+ * @param {MergeDriverParams} args
252
+ * @return {{cleanMerge: boolean, mergedText: string} | Promise<{cleanMerge: boolean, mergedText: string}>}
253
+ */
254
+
242
255
  /**
243
256
  * @callback WalkerMap
244
257
  * @param {string} filename
@@ -1474,6 +1487,21 @@ const getPath = (section, subsection, name) => {
1474
1487
  .join('.')
1475
1488
  };
1476
1489
 
1490
+ const normalizePath$1 = path => {
1491
+ const pathSegments = path.split('.');
1492
+ const section = pathSegments.shift();
1493
+ const name = pathSegments.pop();
1494
+ const subsection = pathSegments.length ? pathSegments.join('.') : undefined;
1495
+
1496
+ return {
1497
+ section,
1498
+ subsection,
1499
+ name,
1500
+ path: getPath(section, subsection, name),
1501
+ sectionPath: getPath(section, subsection, null),
1502
+ }
1503
+ };
1504
+
1477
1505
  const findLastIndex = (array, callback) => {
1478
1506
  return array.reduce((lastIndex, item, index) => {
1479
1507
  return callback(item) ? index : lastIndex
@@ -1513,8 +1541,9 @@ class GitConfig {
1513
1541
  }
1514
1542
 
1515
1543
  async get(path, getall = false) {
1544
+ const normalizedPath = normalizePath$1(path).path;
1516
1545
  const allValues = this.parsedConfig
1517
- .filter(config => config.path === path.toLowerCase())
1546
+ .filter(config => config.path === normalizedPath)
1518
1547
  .map(({ section, name, value }) => {
1519
1548
  const fn = schema[section] && schema[section][name];
1520
1549
  return fn ? fn(value) : value
@@ -1544,9 +1573,16 @@ class GitConfig {
1544
1573
  }
1545
1574
 
1546
1575
  async set(path, value, append = false) {
1576
+ const {
1577
+ section,
1578
+ subsection,
1579
+ name,
1580
+ path: normalizedPath,
1581
+ sectionPath,
1582
+ } = normalizePath$1(path);
1547
1583
  const configIndex = findLastIndex(
1548
1584
  this.parsedConfig,
1549
- config => config.path === path.toLowerCase()
1585
+ config => config.path === normalizedPath
1550
1586
  );
1551
1587
  if (value == null) {
1552
1588
  if (configIndex !== -1) {
@@ -1555,7 +1591,9 @@ class GitConfig {
1555
1591
  } else {
1556
1592
  if (configIndex !== -1) {
1557
1593
  const config = this.parsedConfig[configIndex];
1594
+ // Name should be overwritten in case the casing changed
1558
1595
  const modifiedConfig = Object.assign({}, config, {
1596
+ name,
1559
1597
  value,
1560
1598
  modified: true,
1561
1599
  });
@@ -1565,13 +1603,6 @@ class GitConfig {
1565
1603
  this.parsedConfig[configIndex] = modifiedConfig;
1566
1604
  }
1567
1605
  } else {
1568
- const pathSegments = path.split('.');
1569
- const section = pathSegments.shift().toLowerCase();
1570
- const name = pathSegments.pop();
1571
- const subsection = pathSegments.length
1572
- ? pathSegments.join('.').toLowerCase()
1573
- : undefined;
1574
- const sectionPath = subsection ? section + '.' + subsection : section;
1575
1606
  const sectionIndex = this.parsedConfig.findIndex(
1576
1607
  config => config.path === sectionPath
1577
1608
  );
@@ -1581,7 +1612,7 @@ class GitConfig {
1581
1612
  name,
1582
1613
  value,
1583
1614
  modified: true,
1584
- path: getPath(section, subsection, name),
1615
+ path: normalizedPath,
1585
1616
  };
1586
1617
  if (SECTION_REGEX.test(section) && VARIABLE_NAME_REGEX.test(name)) {
1587
1618
  if (sectionIndex >= 0) {
@@ -1593,7 +1624,7 @@ class GitConfig {
1593
1624
  section,
1594
1625
  subsection,
1595
1626
  modified: true,
1596
- path: getPath(section, subsection, null),
1627
+ path: sectionPath,
1597
1628
  };
1598
1629
  this.parsedConfig.push(newSection, newConfig);
1599
1630
  }
@@ -1609,6 +1640,10 @@ class GitConfig {
1609
1640
  return line
1610
1641
  }
1611
1642
  if (name != null && value != null) {
1643
+ if (typeof value === 'string' && /[#;]/.test(value)) {
1644
+ // A `#` or `;` symbol denotes a comment, so we have to wrap it in double quotes
1645
+ return `\t${name} = "${value}"`
1646
+ }
1612
1647
  return `\t${name} = ${value}`
1613
1648
  }
1614
1649
  if (subsection != null) {
@@ -3180,6 +3215,21 @@ class MergeNotSupportedError extends BaseError {
3180
3215
  /** @type {'MergeNotSupportedError'} */
3181
3216
  MergeNotSupportedError.code = 'MergeNotSupportedError';
3182
3217
 
3218
+ class MergeConflictError extends BaseError {
3219
+ /**
3220
+ * @param {Array<string>} filepaths
3221
+ */
3222
+ constructor(filepaths) {
3223
+ super(
3224
+ `Automatic merge failed with one or more merge conflicts in the following files: ${filepaths.toString()}. Fix conflicts then commit the result.`
3225
+ );
3226
+ this.code = this.name = MergeConflictError.code;
3227
+ this.data = { filepaths };
3228
+ }
3229
+ }
3230
+ /** @type {'MergeConflictError'} */
3231
+ MergeConflictError.code = 'MergeConflictError';
3232
+
3183
3233
  class MissingNameError extends BaseError {
3184
3234
  /**
3185
3235
  * @param {'author'|'committer'|'tagger'} role
@@ -3350,6 +3400,7 @@ var Errors = /*#__PURE__*/Object.freeze({
3350
3400
  InvalidRefNameError: InvalidRefNameError,
3351
3401
  MaxDepthError: MaxDepthError,
3352
3402
  MergeNotSupportedError: MergeNotSupportedError,
3403
+ MergeConflictError: MergeConflictError,
3353
3404
  MissingNameError: MissingNameError,
3354
3405
  MissingParameterError: MissingParameterError,
3355
3406
  MultipleGitError: MultipleGitError,
@@ -4611,7 +4662,15 @@ async function _commit({
4611
4662
  // Probably an initial commit
4612
4663
  parent = [];
4613
4664
  }
4665
+ } else {
4666
+ // ensure that the parents are oids, not refs
4667
+ parent = await Promise.all(
4668
+ parent.map(p => {
4669
+ return GitRefManager.resolve({ fs, gitdir, ref: p })
4670
+ })
4671
+ );
4614
4672
  }
4673
+
4615
4674
  let comm = GitCommit.from({
4616
4675
  tree,
4617
4676
  parent,
@@ -6924,8 +6983,8 @@ function filterCapabilities(server, client) {
6924
6983
 
6925
6984
  const pkg = {
6926
6985
  name: 'isomorphic-git',
6927
- version: '1.17.1',
6928
- agent: 'git/isomorphic-git@1.17.1',
6986
+ version: '1.18.0',
6987
+ agent: 'git/isomorphic-git@1.18.0',
6929
6988
  };
6930
6989
 
6931
6990
  class FIFO {
@@ -8401,16 +8460,14 @@ async function _findMergeBase({ fs, cache, gitdir, oids }) {
8401
8460
 
8402
8461
  const LINEBREAKS = /^.*(\r?\n|$)/gm;
8403
8462
 
8404
- function mergeFile({
8405
- ourContent,
8406
- baseContent,
8407
- theirContent,
8408
- ourName = 'ours',
8409
- baseName = 'base',
8410
- theirName = 'theirs',
8411
- format = 'diff',
8412
- markerSize = 7,
8413
- }) {
8463
+ function mergeFile({ branches, contents }) {
8464
+ const ourName = branches[1];
8465
+ const theirName = branches[2];
8466
+
8467
+ const baseContent = contents[0];
8468
+ const ourContent = contents[1];
8469
+ const theirContent = contents[2];
8470
+
8414
8471
  const ours = ourContent.match(LINEBREAKS);
8415
8472
  const base = baseContent.match(LINEBREAKS);
8416
8473
  const theirs = theirContent.match(LINEBREAKS);
@@ -8418,9 +8475,12 @@ function mergeFile({
8418
8475
  // Here we let the diff3 library do the heavy lifting.
8419
8476
  const result = diff3Merge(ours, base, theirs);
8420
8477
 
8478
+ const markerSize = 7;
8479
+
8421
8480
  // Here we note whether there are conflicts and format the results
8422
8481
  let mergedText = '';
8423
8482
  let cleanMerge = true;
8483
+
8424
8484
  for (const item of result) {
8425
8485
  if (item.ok) {
8426
8486
  mergedText += item.ok.join('');
@@ -8429,10 +8489,7 @@ function mergeFile({
8429
8489
  cleanMerge = false;
8430
8490
  mergedText += `${'<'.repeat(markerSize)} ${ourName}\n`;
8431
8491
  mergedText += item.conflict.a.join('');
8432
- if (format === 'diff3') {
8433
- mergedText += `${'|'.repeat(markerSize)} ${baseName}\n`;
8434
- mergedText += item.conflict.o.join('');
8435
- }
8492
+
8436
8493
  mergedText += `${'='.repeat(markerSize)}\n`;
8437
8494
  mergedText += item.conflict.b.join('');
8438
8495
  mergedText += `${'>'.repeat(markerSize)} ${theirName}\n`;
@@ -8458,6 +8515,8 @@ function mergeFile({
8458
8515
  * @param {string} [args.baseName='base'] - The name to use in conflicted files (in diff3 format) for the base hunks
8459
8516
  * @param {string} [args.theirName='theirs'] - The name to use in conflicted files for their hunks
8460
8517
  * @param {boolean} [args.dryRun=false]
8518
+ * @param {boolean} [args.abortOnConflict=false]
8519
+ * @param {MergeDriverCallback} [args.mergeDriver]
8461
8520
  *
8462
8521
  * @returns {Promise<string>} - The SHA-1 object id of the merged tree
8463
8522
  *
@@ -8474,11 +8533,17 @@ async function mergeTree({
8474
8533
  baseName = 'base',
8475
8534
  theirName = 'theirs',
8476
8535
  dryRun = false,
8536
+ abortOnConflict = true,
8537
+ mergeDriver,
8477
8538
  }) {
8478
8539
  const ourTree = TREE({ ref: ourOid });
8479
8540
  const baseTree = TREE({ ref: baseOid });
8480
8541
  const theirTree = TREE({ ref: theirOid });
8481
8542
 
8543
+ const unmergedFiles = [];
8544
+
8545
+ let cleanMerge = true;
8546
+
8482
8547
  const results = await _walk({
8483
8548
  fs,
8484
8549
  cache,
@@ -8539,6 +8604,11 @@ async function mergeTree({
8539
8604
  ourName,
8540
8605
  baseName,
8541
8606
  theirName,
8607
+ mergeDriver,
8608
+ }).then(r => {
8609
+ cleanMerge = r.cleanMerge;
8610
+ unmergedFiles.push(filepath);
8611
+ return r.mergeResult
8542
8612
  })
8543
8613
  }
8544
8614
  // all other types of conflicts fail
@@ -8574,6 +8644,29 @@ async function mergeTree({
8574
8644
  return parent
8575
8645
  },
8576
8646
  });
8647
+
8648
+ if (!cleanMerge) {
8649
+ if (dir && !abortOnConflict) {
8650
+ await _walk({
8651
+ fs,
8652
+ cache,
8653
+ dir,
8654
+ gitdir,
8655
+ trees: [TREE({ ref: results.oid })],
8656
+ map: async function(filepath, [entry]) {
8657
+ const path = `${dir}/${filepath}`;
8658
+ if ((await entry.type()) === 'blob') {
8659
+ const mode = await entry.mode();
8660
+ const content = new TextDecoder().decode(await entry.content());
8661
+ await fs.write(path, content, { mode });
8662
+ }
8663
+ return true
8664
+ },
8665
+ });
8666
+ }
8667
+ throw new MergeConflictError(unmergedFiles)
8668
+ }
8669
+
8577
8670
  return results.oid
8578
8671
  }
8579
8672
 
@@ -8612,9 +8705,8 @@ async function modified(entry, base) {
8612
8705
  * @param {string} [args.ourName]
8613
8706
  * @param {string} [args.baseName]
8614
8707
  * @param {string} [args.theirName]
8615
- * @param {string} [args.format]
8616
- * @param {number} [args.markerSize]
8617
8708
  * @param {boolean} [args.dryRun = false]
8709
+ * @param {MergeDriverCallback} [args.mergeDriver]
8618
8710
  *
8619
8711
  */
8620
8712
  async function mergeBlobs({
@@ -8627,9 +8719,8 @@ async function mergeBlobs({
8627
8719
  ourName,
8628
8720
  theirName,
8629
8721
  baseName,
8630
- format,
8631
- markerSize,
8632
8722
  dryRun,
8723
+ mergeDriver = mergeFile,
8633
8724
  }) {
8634
8725
  const type = 'blob';
8635
8726
  // Compute the new mode.
@@ -8640,30 +8731,33 @@ async function mergeBlobs({
8640
8731
  : await ours.mode();
8641
8732
  // The trivial case: nothing to merge except maybe mode
8642
8733
  if ((await ours.oid()) === (await theirs.oid())) {
8643
- return { mode, path, oid: await ours.oid(), type }
8734
+ return {
8735
+ cleanMerge: true,
8736
+ mergeResult: { mode, path, oid: await ours.oid(), type },
8737
+ }
8644
8738
  }
8645
8739
  // if only one side made oid changes, return that side's oid
8646
8740
  if ((await ours.oid()) === (await base.oid())) {
8647
- return { mode, path, oid: await theirs.oid(), type }
8741
+ return {
8742
+ cleanMerge: true,
8743
+ mergeResult: { mode, path, oid: await theirs.oid(), type },
8744
+ }
8648
8745
  }
8649
8746
  if ((await theirs.oid()) === (await base.oid())) {
8650
- return { mode, path, oid: await ours.oid(), type }
8747
+ return {
8748
+ cleanMerge: true,
8749
+ mergeResult: { mode, path, oid: await ours.oid(), type },
8750
+ }
8651
8751
  }
8652
8752
  // if both sides made changes do a merge
8653
- const { mergedText, cleanMerge } = mergeFile({
8654
- ourContent: Buffer.from(await ours.content()).toString('utf8'),
8655
- baseContent: Buffer.from(await base.content()).toString('utf8'),
8656
- theirContent: Buffer.from(await theirs.content()).toString('utf8'),
8657
- ourName,
8658
- theirName,
8659
- baseName,
8660
- format,
8661
- markerSize,
8753
+ const ourContent = Buffer.from(await ours.content()).toString('utf8');
8754
+ const baseContent = Buffer.from(await base.content()).toString('utf8');
8755
+ const theirContent = Buffer.from(await theirs.content()).toString('utf8');
8756
+ const { mergedText, cleanMerge } = await mergeDriver({
8757
+ branches: [baseName, ourName, theirName],
8758
+ contents: [baseContent, ourContent, theirContent],
8759
+ path,
8662
8760
  });
8663
- if (!cleanMerge) {
8664
- // all other types of conflicts fail
8665
- throw new MergeNotSupportedError()
8666
- }
8667
8761
  const oid = await _writeObject({
8668
8762
  fs,
8669
8763
  gitdir,
@@ -8671,7 +8765,8 @@ async function mergeBlobs({
8671
8765
  object: Buffer.from(mergedText, 'utf8'),
8672
8766
  dryRun,
8673
8767
  });
8674
- return { mode, path, oid, type }
8768
+
8769
+ return { cleanMerge, mergeResult: { mode, path, oid, type } }
8675
8770
  }
8676
8771
 
8677
8772
  // @ts-check
@@ -8699,6 +8794,7 @@ async function mergeBlobs({
8699
8794
  * @param {boolean} args.fastForwardOnly
8700
8795
  * @param {boolean} args.dryRun
8701
8796
  * @param {boolean} args.noUpdateBranch
8797
+ * @param {boolean} args.abortOnConflict
8702
8798
  * @param {string} [args.message]
8703
8799
  * @param {Object} args.author
8704
8800
  * @param {string} args.author.name
@@ -8712,6 +8808,7 @@ async function mergeBlobs({
8712
8808
  * @param {number} args.committer.timezoneOffset
8713
8809
  * @param {string} [args.signingKey]
8714
8810
  * @param {SignCallback} [args.onSign] - a PGP signing implementation
8811
+ * @param {MergeDriverCallback} [args.mergeDriver]
8715
8812
  *
8716
8813
  * @returns {Promise<MergeResult>} Resolves to a description of the merge operation
8717
8814
  *
@@ -8719,6 +8816,7 @@ async function mergeBlobs({
8719
8816
  async function _merge({
8720
8817
  fs,
8721
8818
  cache,
8819
+ dir,
8722
8820
  gitdir,
8723
8821
  ours,
8724
8822
  theirs,
@@ -8726,11 +8824,13 @@ async function _merge({
8726
8824
  fastForwardOnly = false,
8727
8825
  dryRun = false,
8728
8826
  noUpdateBranch = false,
8827
+ abortOnConflict = true,
8729
8828
  message,
8730
8829
  author,
8731
8830
  committer,
8732
8831
  signingKey,
8733
8832
  onSign,
8833
+ mergeDriver,
8734
8834
  }) {
8735
8835
  if (ours === undefined) {
8736
8836
  ours = await _currentBranch({ fs, gitdir, fullname: true });
@@ -8790,14 +8890,17 @@ async function _merge({
8790
8890
  const tree = await mergeTree({
8791
8891
  fs,
8792
8892
  cache,
8893
+ dir,
8793
8894
  gitdir,
8794
8895
  ourOid,
8795
8896
  theirOid,
8796
8897
  baseOid,
8797
- ourName: ours,
8898
+ ourName: abbreviateRef(ours),
8798
8899
  baseName: 'base',
8799
- theirName: theirs,
8900
+ theirName: abbreviateRef(theirs),
8800
8901
  dryRun,
8902
+ abortOnConflict,
8903
+ mergeDriver,
8801
8904
  });
8802
8905
  if (!message) {
8803
8906
  message = `Merge branch '${abbreviateRef(theirs)}' into ${abbreviateRef(
@@ -10765,11 +10868,11 @@ async function _log({
10765
10868
  }
10766
10869
  }
10767
10870
  if (!found) {
10768
- if (!force && !follow) throw e
10769
10871
  if (isOk && lastFileOid) {
10770
10872
  commits.push(lastCommit);
10771
- // break
10873
+ if (!force) break
10772
10874
  }
10875
+ if (!force && !follow) throw e
10773
10876
  }
10774
10877
  lastCommit = commit;
10775
10878
  isOk = false;
@@ -10889,15 +10992,63 @@ async function log({
10889
10992
  /**
10890
10993
  * Merge two branches
10891
10994
  *
10892
- * ## Limitations
10893
- *
10894
- * Currently it does not support incomplete merges. That is, if there are merge conflicts it cannot solve
10895
- * with the built in diff3 algorithm it will not modify the working dir, and will throw a [`MergeNotSupportedError`](./errors.md#mergenotsupportedError) error.
10896
- *
10897
10995
  * Currently it will fail if multiple candidate merge bases are found. (It doesn't yet implement the recursive merge strategy.)
10898
10996
  *
10899
10997
  * Currently it does not support selecting alternative merge strategies.
10900
10998
  *
10999
+ * Currently it is not possible to abort an incomplete merge. To restore the worktree to a clean state, you will need to checkout an earlier commit.
11000
+ *
11001
+ * Currently it does not directly support the behavior of `git merge --continue`. To complete a merge after manual conflict resolution, you will need to add and commit the files manually, and specify the appropriate parent commits.
11002
+ *
11003
+ * ## Manually resolving merge conflicts
11004
+ * By default, if isomorphic-git encounters a merge conflict it cannot resolve using the builtin diff3 algorithm or provided merge driver, it will abort and throw a `MergeNotSupportedError`.
11005
+ * This leaves the index and working tree untouched.
11006
+ *
11007
+ * When `abortOnConflict` is set to `false`, and a merge conflict cannot be automatically resolved, a `MergeConflictError` is thrown and the results of the incomplete merge will be written to the working directory.
11008
+ * This includes conflict markers in files with unresolved merge conflicts.
11009
+ *
11010
+ * To complete the merge, edit the conflicting files as you see fit, and then add and commit the resolved merge.
11011
+ *
11012
+ * For a proper merge commit, be sure to specify the branches or commits you are merging in the `parent` argument to `git.commit`.
11013
+ * For example, say we are merging the branch `feature` into the branch `main` and there is a conflict we want to resolve manually.
11014
+ * The flow would look like this:
11015
+ *
11016
+ * ```
11017
+ * await git.merge({
11018
+ * fs,
11019
+ * dir,
11020
+ * ours: 'main',
11021
+ * theirs: 'feature',
11022
+ * abortOnConflict: false,
11023
+ * }).catch(e => {
11024
+ * if (e instanceof Errors.MergeConflictError) {
11025
+ * console.log(
11026
+ * 'Automatic merge failed for the following files: '
11027
+ * + `${e.data}. `
11028
+ * + 'Resolve these conflicts and then commit your changes.'
11029
+ * )
11030
+ * } else throw e
11031
+ * })
11032
+ *
11033
+ * // This is the where we manually edit the files that have been written to the working directory
11034
+ * // ...
11035
+ * // Files have been edited and we are ready to commit
11036
+ *
11037
+ * await git.add({
11038
+ * fs,
11039
+ * dir,
11040
+ * filepath: '.',
11041
+ * })
11042
+ *
11043
+ * await git.commit({
11044
+ * fs,
11045
+ * dir,
11046
+ * ref: 'main',
11047
+ * message: "Merge branch 'feature' into main",
11048
+ * parent: ['main', 'feature'], // Be sure to specify the parents when creating a merge commit
11049
+ * })
11050
+ * ```
11051
+ *
10901
11052
  * @param {object} args
10902
11053
  * @param {FsClient} args.fs - a file system client
10903
11054
  * @param {SignCallback} [args.onSign] - a PGP signing implementation
@@ -10909,6 +11060,7 @@ async function log({
10909
11060
  * @param {boolean} [args.fastForwardOnly = false] - If true, then non-fast-forward merges will throw an Error instead of performing a merge.
10910
11061
  * @param {boolean} [args.dryRun = false] - If true, simulates a merge so you can test whether it would succeed.
10911
11062
  * @param {boolean} [args.noUpdateBranch = false] - If true, does not update the branch pointer after creating the commit.
11063
+ * @param {boolean} [args.abortOnConflict = true] - If true, merges with conflicts will not update the worktree or index.
10912
11064
  * @param {string} [args.message] - Overrides the default auto-generated merge commit message
10913
11065
  * @param {Object} [args.author] - passed to [commit](commit.md) when creating a merge commit
10914
11066
  * @param {string} [args.author.name] - Default is `user.name` config.
@@ -10922,6 +11074,7 @@ async function log({
10922
11074
  * @param {number} [args.committer.timezoneOffset] - Set the committer timezone offset field. This is the difference, in minutes, from the current timezone to UTC. Default is `(new Date()).getTimezoneOffset()`.
10923
11075
  * @param {string} [args.signingKey] - passed to [commit](commit.md) when creating a merge commit
10924
11076
  * @param {object} [args.cache] - a [cache](cache.md) object
11077
+ * @param {MergeDriverCallback} [args.mergeDriver] - a [merge driver](mergeDriver.md) implementation
10925
11078
  *
10926
11079
  * @returns {Promise<MergeResult>} Resolves to a description of the merge operation
10927
11080
  * @see MergeResult
@@ -10947,11 +11100,13 @@ async function merge({
10947
11100
  fastForwardOnly = false,
10948
11101
  dryRun = false,
10949
11102
  noUpdateBranch = false,
11103
+ abortOnConflict = true,
10950
11104
  message,
10951
11105
  author: _author,
10952
11106
  committer: _committer,
10953
11107
  signingKey,
10954
11108
  cache = {},
11109
+ mergeDriver,
10955
11110
  }) {
10956
11111
  try {
10957
11112
  assertParameter('fs', _fs);
@@ -10978,6 +11133,7 @@ async function merge({
10978
11133
  return await _merge({
10979
11134
  fs,
10980
11135
  cache,
11136
+ dir,
10981
11137
  gitdir,
10982
11138
  ours,
10983
11139
  theirs,
@@ -10985,11 +11141,13 @@ async function merge({
10985
11141
  fastForwardOnly,
10986
11142
  dryRun,
10987
11143
  noUpdateBranch,
11144
+ abortOnConflict,
10988
11145
  message,
10989
11146
  author,
10990
11147
  committer,
10991
11148
  signingKey,
10992
11149
  onSign,
11150
+ mergeDriver,
10993
11151
  })
10994
11152
  } catch (err) {
10995
11153
  err.caller = 'git.merge';