isomorphic-git 1.13.0 → 1.15.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/index.js CHANGED
@@ -756,6 +756,10 @@ class GitIndex {
756
756
  this._dirty = true;
757
757
  }
758
758
 
759
+ has({ filepath }) {
760
+ return this._entries.has(filepath)
761
+ }
762
+
759
763
  render() {
760
764
  return this.entries
761
765
  .map(entry => `${entry.mode.toString(8)} ${entry.oid} ${entry.path}`)
@@ -865,7 +869,7 @@ class GitIndexManager {
865
869
  const filepath = `${gitdir}/index`;
866
870
  if (lock === null) lock = new AsyncLock({ maxPending: Infinity });
867
871
  let result;
868
- await lock.acquire(filepath, async function() {
872
+ await lock.acquire(filepath, async () => {
869
873
  // Acquire a file lock while we're reading the index
870
874
  // to make sure other processes aren't writing to it
871
875
  // simultaneously, which could result in a corrupted index.
@@ -885,6 +889,7 @@ class GitIndexManager {
885
889
  index._dirty = false;
886
890
  }
887
891
  });
892
+
888
893
  return result
889
894
  }
890
895
  }
@@ -3112,12 +3117,14 @@ HttpError.code = 'HttpError';
3112
3117
 
3113
3118
  class InvalidFilepathError extends BaseError {
3114
3119
  /**
3115
- * @param {'leading-slash'|'trailing-slash'} [reason]
3120
+ * @param {'leading-slash'|'trailing-slash'|'directory'} [reason]
3116
3121
  */
3117
3122
  constructor(reason) {
3118
3123
  let message = 'invalid filepath';
3119
3124
  if (reason === 'leading-slash' || reason === 'trailing-slash') {
3120
3125
  message = `"filepath" parameter should not include leading or trailing directory separators because these can cause problems on some platforms.`;
3126
+ } else if (reason === 'directory') {
3127
+ message = `"filepath" should not be a directory.`;
3121
3128
  }
3122
3129
  super(message);
3123
3130
  this.code = this.name = InvalidFilepathError.code;
@@ -4016,6 +4023,23 @@ function WORKDIR() {
4016
4023
 
4017
4024
  // @ts-check
4018
4025
 
4026
+ class MultipleGitError extends BaseError {
4027
+ /**
4028
+ * @param {Error[]} errors
4029
+ * @param {string} message
4030
+ */
4031
+ constructor(errors) {
4032
+ super(
4033
+ `There are multiple errors that were thrown by the method. Please refer to the "errors" property to see more`
4034
+ );
4035
+ this.code = this.name = MultipleGitError.code;
4036
+ this.data = { errors };
4037
+ this.errors = errors;
4038
+ }
4039
+ }
4040
+ /** @type {'MultipleGitError'} */
4041
+ MultipleGitError.code = 'MultipleGitError';
4042
+
4019
4043
  // I'm putting this in a Manager because I reckon it could benefit
4020
4044
  // from a LOT of cacheing.
4021
4045
  class GitIgnoreManager {
@@ -4406,12 +4430,6 @@ function assertParameter(name, value) {
4406
4430
  }
4407
4431
  }
4408
4432
 
4409
- function posixifyPathBuffer(buffer) {
4410
- let idx;
4411
- while (~(idx = buffer.indexOf(92))) buffer[idx] = 47;
4412
- return buffer
4413
- }
4414
-
4415
4433
  // @ts-check
4416
4434
 
4417
4435
  /**
@@ -4421,7 +4439,7 @@ function posixifyPathBuffer(buffer) {
4421
4439
  * @param {FsClient} args.fs - a file system implementation
4422
4440
  * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
4423
4441
  * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
4424
- * @param {string} args.filepath - The path to the file to add to the index
4442
+ * @param {string|string[]} args.filepath - The path to the file to add to the index
4425
4443
  * @param {object} [args.cache] - a [cache](cache.md) object
4426
4444
  *
4427
4445
  * @returns {Promise<void>} Resolves successfully once the git index has been updated
@@ -4443,11 +4461,11 @@ async function add({
4443
4461
  assertParameter('fs', _fs);
4444
4462
  assertParameter('dir', dir);
4445
4463
  assertParameter('gitdir', gitdir);
4446
- assertParameter('filepath', filepath);
4464
+ assertParameter('filepaths', filepath);
4447
4465
 
4448
4466
  const fs = new FileSystem(_fs);
4449
- await GitIndexManager.acquire({ fs, gitdir, cache }, async function(index) {
4450
- await addToIndex({ dir, gitdir, fs, filepath, index });
4467
+ await GitIndexManager.acquire({ fs, gitdir, cache }, async index => {
4468
+ return addToIndex({ dir, gitdir, fs, filepath, index })
4451
4469
  });
4452
4470
  } catch (err) {
4453
4471
  err.caller = 'git.add';
@@ -4457,29 +4475,56 @@ async function add({
4457
4475
 
4458
4476
  async function addToIndex({ dir, gitdir, fs, filepath, index }) {
4459
4477
  // TODO: Should ignore UNLESS it's already in the index.
4460
- const ignored = await GitIgnoreManager.isIgnored({
4461
- fs,
4462
- dir,
4463
- gitdir,
4464
- filepath,
4478
+ filepath = Array.isArray(filepath) ? filepath : [filepath];
4479
+ const promises = filepath.map(async currentFilepath => {
4480
+ const ignored = await GitIgnoreManager.isIgnored({
4481
+ fs,
4482
+ dir,
4483
+ gitdir,
4484
+ filepath: currentFilepath,
4485
+ });
4486
+ if (ignored) return
4487
+ const stats = await fs.lstat(join(dir, currentFilepath));
4488
+ if (!stats) throw new NotFoundError(currentFilepath)
4489
+
4490
+ if (stats.isDirectory()) {
4491
+ const children = await fs.readdir(join(dir, currentFilepath));
4492
+ const promises = children.map(child =>
4493
+ addToIndex({
4494
+ dir,
4495
+ gitdir,
4496
+ fs,
4497
+ filepath: [join(currentFilepath, child)],
4498
+ index,
4499
+ })
4500
+ );
4501
+ await Promise.all(promises);
4502
+ } else {
4503
+ const object = stats.isSymbolicLink()
4504
+ ? await fs.readlink(join(dir, currentFilepath))
4505
+ : await fs.read(join(dir, currentFilepath));
4506
+ if (object === null) throw new NotFoundError(currentFilepath)
4507
+ const oid = await _writeObject({ fs, gitdir, type: 'blob', object });
4508
+ index.insert({ filepath: currentFilepath, stats, oid });
4509
+ }
4465
4510
  });
4466
- if (ignored) return
4467
- const stats = await fs.lstat(join(dir, filepath));
4468
- if (!stats) throw new NotFoundError(filepath)
4469
- if (stats.isDirectory()) {
4470
- const children = await fs.readdir(join(dir, filepath));
4471
- const promises = children.map(child =>
4472
- addToIndex({ dir, gitdir, fs, filepath: join(filepath, child), index })
4473
- );
4474
- await Promise.all(promises);
4475
- } else {
4476
- const object = stats.isSymbolicLink()
4477
- ? await fs.readlink(join(dir, filepath)).then(posixifyPathBuffer)
4478
- : await fs.read(join(dir, filepath));
4479
- if (object === null) throw new NotFoundError(filepath)
4480
- const oid = await _writeObject({ fs, gitdir, type: 'blob', object });
4481
- index.insert({ filepath, stats, oid });
4511
+
4512
+ const settledPromises = await Promise.allSettled(promises);
4513
+ const rejectedPromises = settledPromises
4514
+ .filter(settle => settle.status === 'rejected')
4515
+ .map(settle => settle.reason);
4516
+ if (rejectedPromises.length > 1) {
4517
+ throw new MultipleGitError(rejectedPromises)
4482
4518
  }
4519
+ if (rejectedPromises.length === 1) {
4520
+ throw rejectedPromises[0]
4521
+ }
4522
+
4523
+ const fulfilledPromises = settledPromises
4524
+ .filter(settle => settle.status === 'fulfilled' && settle.value)
4525
+ .map(settle => settle.value);
4526
+
4527
+ return fulfilledPromises
4483
4528
  }
4484
4529
 
4485
4530
  // @ts-check
@@ -5834,7 +5879,7 @@ async function analyze({
5834
5879
 
5835
5880
  // This is a kind of silly pattern but it worked so well for me in the past
5836
5881
  // and it makes intuitively demonstrating exhaustiveness so *easy*.
5837
- // This checks for the presense and/or absense of each of the 3 entries,
5882
+ // This checks for the presense and/or absence of each of the 3 entries,
5838
5883
  // converts that to a 3-bit binary representation, and then handles
5839
5884
  // every possible combination (2^3 or 8 cases) with a lookup table.
5840
5885
  const key = [!!stage, !!commit, !!workdir].map(Number).join('');
@@ -6867,8 +6912,8 @@ function filterCapabilities(server, client) {
6867
6912
 
6868
6913
  const pkg = {
6869
6914
  name: 'isomorphic-git',
6870
- version: '1.13.0',
6871
- agent: 'git/isomorphic-git@1.13.0',
6915
+ version: '1.15.0',
6916
+ agent: 'git/isomorphic-git@1.15.0',
6872
6917
  };
6873
6918
 
6874
6919
  class FIFO {
@@ -13338,27 +13383,37 @@ async function statusMatrix({
13338
13383
  if (!filter(filepath)) return
13339
13384
  }
13340
13385
 
13341
- // For now, just bail on directories
13342
- const headType = head && (await head.type());
13343
- if (headType === 'tree' || headType === 'special') return
13386
+ const [headType, workdirType, stageType] = await Promise.all([
13387
+ head && head.type(),
13388
+ workdir && workdir.type(),
13389
+ stage && stage.type(),
13390
+ ]);
13391
+
13392
+ const isBlob = [headType, workdirType, stageType].includes('blob');
13393
+
13394
+ // For now, bail on directories unless the file is also a blob in another tree
13395
+ if ((headType === 'tree' || headType === 'special') && !isBlob) return
13344
13396
  if (headType === 'commit') return null
13345
13397
 
13346
- const workdirType = workdir && (await workdir.type());
13347
- if (workdirType === 'tree' || workdirType === 'special') return
13398
+ if ((workdirType === 'tree' || workdirType === 'special') && !isBlob)
13399
+ return
13348
13400
 
13349
- const stageType = stage && (await stage.type());
13350
13401
  if (stageType === 'commit') return null
13351
- if (stageType === 'tree' || stageType === 'special') return
13402
+ if ((stageType === 'tree' || stageType === 'special') && !isBlob) return
13352
13403
 
13353
- // Figure out the oids, using the staged oid for the working dir oid if the stats match.
13354
- const headOid = head ? await head.oid() : undefined;
13355
- const stageOid = stage ? await stage.oid() : undefined;
13404
+ // Figure out the oids for files, using the staged oid for the working dir oid if the stats match.
13405
+ const headOid = headType === 'blob' ? await head.oid() : undefined;
13406
+ const stageOid = stageType === 'blob' ? await stage.oid() : undefined;
13356
13407
  let workdirOid;
13357
- if (!head && workdir && !stage) {
13408
+ if (
13409
+ headType !== 'blob' &&
13410
+ workdirType === 'blob' &&
13411
+ stageType !== 'blob'
13412
+ ) {
13358
13413
  // We don't actually NEED the sha. Any sha will do
13359
13414
  // TODO: update this logic to handle N trees instead of just 3.
13360
13415
  workdirOid = '42';
13361
- } else if (workdir) {
13416
+ } else if (workdirType === 'blob') {
13362
13417
  workdirOid = await workdir.oid();
13363
13418
  }
13364
13419
  const entry = [undefined, headOid, workdirOid, stageOid];
@@ -13434,6 +13489,162 @@ async function tag({
13434
13489
 
13435
13490
  // @ts-check
13436
13491
 
13492
+ /**
13493
+ * Register file contents in the working tree or object database to the git index (aka staging area).
13494
+ *
13495
+ * @param {object} args
13496
+ * @param {FsClient} args.fs - a file system client
13497
+ * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
13498
+ * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
13499
+ * @param {string} args.filepath - File to act upon.
13500
+ * @param {string} [args.oid] - OID of the object in the object database to add to the index with the specified filepath.
13501
+ * @param {number} [args.mode = 100644] - The file mode to add the file to the index.
13502
+ * @param {boolean} [args.add] - Adds the specified file to the index if it does not yet exist in the index.
13503
+ * @param {boolean} [args.remove] - Remove the specified file from the index if it does not exist in the workspace anymore.
13504
+ * @param {boolean} [args.force] - Remove the specified file from the index, even if it still exists in the workspace.
13505
+ * @param {object} [args.cache] - a [cache](cache.md) object
13506
+ *
13507
+ * @returns {Promise<string | void>} Resolves successfully with the SHA-1 object id of the object written or updated in the index, or nothing if the file was removed.
13508
+ *
13509
+ * @example
13510
+ * await git.updateIndex({
13511
+ * fs,
13512
+ * dir: '/tutorial',
13513
+ * filepath: 'readme.md'
13514
+ * })
13515
+ *
13516
+ * @example
13517
+ * // Manually create a blob in the object database.
13518
+ * let oid = await git.writeBlob({
13519
+ * fs,
13520
+ * dir: '/tutorial',
13521
+ * blob: new Uint8Array([])
13522
+ * })
13523
+ *
13524
+ * // Write the object in the object database to the index.
13525
+ * await git.updateIndex({
13526
+ * fs,
13527
+ * dir: '/tutorial',
13528
+ * add: true,
13529
+ * filepath: 'readme.md',
13530
+ * oid
13531
+ * })
13532
+ */
13533
+ async function updateIndex({
13534
+ fs: _fs,
13535
+ dir,
13536
+ gitdir = join(dir, '.git'),
13537
+ cache = {},
13538
+ filepath,
13539
+ oid,
13540
+ mode,
13541
+ add,
13542
+ remove,
13543
+ force,
13544
+ }) {
13545
+ try {
13546
+ assertParameter('fs', _fs);
13547
+ assertParameter('gitdir', gitdir);
13548
+ assertParameter('filepath', filepath);
13549
+
13550
+ const fs = new FileSystem(_fs);
13551
+
13552
+ if (remove) {
13553
+ return await GitIndexManager.acquire(
13554
+ { fs, gitdir, cache },
13555
+ async function(index) {
13556
+ let fileStats;
13557
+
13558
+ if (!force) {
13559
+ // Check if the file is still present in the working directory
13560
+ fileStats = await fs.lstat(join(dir, filepath));
13561
+
13562
+ if (fileStats) {
13563
+ // Do nothing if we don't force and the file still exists in the workdir
13564
+ return
13565
+ }
13566
+ }
13567
+
13568
+ // Remove the file from the index if it's forced or the file does not exist
13569
+ index.delete({
13570
+ filepath,
13571
+ });
13572
+ }
13573
+ )
13574
+ }
13575
+
13576
+ // Test if it is a file and exists on disk if `remove` is not provided, only of no oid is provided
13577
+ let fileStats;
13578
+
13579
+ if (!oid) {
13580
+ fileStats = await fs.lstat(join(dir, filepath));
13581
+
13582
+ if (!fileStats) {
13583
+ throw new NotFoundError(
13584
+ `file at "${filepath}" on disk and "remove" not set`
13585
+ )
13586
+ }
13587
+
13588
+ if (fileStats.isDirectory()) {
13589
+ throw new InvalidFilepathError('directory')
13590
+ }
13591
+ }
13592
+
13593
+ return await GitIndexManager.acquire({ fs, gitdir, cache }, async function(
13594
+ index
13595
+ ) {
13596
+ if (!add && !index.has({ filepath })) {
13597
+ // If the index does not contain the filepath yet and `add` is not set, we should throw
13598
+ throw new NotFoundError(
13599
+ `file at "${filepath}" in index and "add" not set`
13600
+ )
13601
+ }
13602
+
13603
+ // By default we use 0 for the stats of the index file
13604
+ let stats = {
13605
+ ctime: new Date(0),
13606
+ mtime: new Date(0),
13607
+ dev: 0,
13608
+ ino: 0,
13609
+ mode,
13610
+ uid: 0,
13611
+ gid: 0,
13612
+ size: 0,
13613
+ };
13614
+
13615
+ if (!oid) {
13616
+ stats = fileStats;
13617
+
13618
+ // Write the file to the object database
13619
+ const object = stats.isSymbolicLink()
13620
+ ? await fs.readlink(join(dir, filepath))
13621
+ : await fs.read(join(dir, filepath));
13622
+
13623
+ oid = await _writeObject({
13624
+ fs,
13625
+ gitdir,
13626
+ type: 'blob',
13627
+ format: 'content',
13628
+ object,
13629
+ });
13630
+ }
13631
+
13632
+ index.insert({
13633
+ filepath,
13634
+ oid: oid,
13635
+ stats,
13636
+ });
13637
+
13638
+ return oid
13639
+ })
13640
+ } catch (err) {
13641
+ err.caller = 'git.updateIndex';
13642
+ throw err
13643
+ }
13644
+ }
13645
+
13646
+ // @ts-check
13647
+
13437
13648
  /**
13438
13649
  * Return the version number of isomorphic-git
13439
13650
  *
@@ -14204,6 +14415,7 @@ var index = {
14204
14415
  removeNote,
14205
14416
  renameBranch,
14206
14417
  resetIndex,
14418
+ updateIndex,
14207
14419
  resolveRef,
14208
14420
  status,
14209
14421
  statusMatrix,
@@ -14219,4 +14431,4 @@ var index = {
14219
14431
  };
14220
14432
 
14221
14433
  export default index;
14222
- export { Errors, STAGE, TREE, WORKDIR, add, addNote, addRemote, annotatedTag, branch, checkout, clone, commit, currentBranch, deleteBranch, deleteRef, deleteRemote, deleteTag, expandOid, expandRef, fastForward, fetch, findMergeBase, findRoot, getConfig, getConfigAll, getRemoteInfo, getRemoteInfo2, hashBlob, indexPack, init, isDescendent, isIgnored, listBranches, listFiles, listNotes, listRemotes, listServerRefs, listTags, log, merge, packObjects, pull, push, readBlob, readCommit, readNote, readObject, readTag, readTree, remove, removeNote, renameBranch, resetIndex, resolveRef, setConfig, status, statusMatrix, tag, version, walk, writeBlob, writeCommit, writeObject, writeRef, writeTag, writeTree };
14434
+ export { Errors, STAGE, TREE, WORKDIR, add, addNote, addRemote, annotatedTag, branch, checkout, clone, commit, currentBranch, deleteBranch, deleteRef, deleteRemote, deleteTag, expandOid, expandRef, fastForward, fetch, findMergeBase, findRoot, getConfig, getConfigAll, getRemoteInfo, getRemoteInfo2, hashBlob, indexPack, init, isDescendent, isIgnored, listBranches, listFiles, listNotes, listRemotes, listServerRefs, listTags, log, merge, packObjects, pull, push, readBlob, readCommit, readNote, readObject, readTag, readTree, remove, removeNote, renameBranch, resetIndex, resolveRef, setConfig, status, statusMatrix, tag, updateIndex, version, walk, writeBlob, writeCommit, writeObject, writeRef, writeTag, writeTree };
@@ -685,6 +685,7 @@ declare namespace index {
685
685
  export { removeNote };
686
686
  export { renameBranch };
687
687
  export { resetIndex };
688
+ export { updateIndex };
688
689
  export { resolveRef };
689
690
  export { status };
690
691
  export { statusMatrix };
@@ -751,7 +752,7 @@ export function WORKDIR(): Walker;
751
752
  * @param {FsClient} args.fs - a file system implementation
752
753
  * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
753
754
  * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
754
- * @param {string} args.filepath - The path to the file to add to the index
755
+ * @param {string|string[]} args.filepath - The path to the file to add to the index
755
756
  * @param {object} [args.cache] - a [cache](cache.md) object
756
757
  *
757
758
  * @returns {Promise<void>} Resolves successfully once the git index has been updated
@@ -766,7 +767,7 @@ export function add({ fs: _fs, dir, gitdir, filepath, cache, }: {
766
767
  fs: CallbackFsClient | PromiseFsClient;
767
768
  dir: string;
768
769
  gitdir?: string;
769
- filepath: string;
770
+ filepath: string | string[];
770
771
  cache?: any;
771
772
  }): Promise<void>;
772
773
  /**
@@ -3107,6 +3108,59 @@ export function tag({ fs: _fs, dir, gitdir, ref, object, force, }: {
3107
3108
  object?: string;
3108
3109
  force?: boolean;
3109
3110
  }): Promise<void>;
3111
+ /**
3112
+ * Register file contents in the working tree or object database to the git index (aka staging area).
3113
+ *
3114
+ * @param {object} args
3115
+ * @param {FsClient} args.fs - a file system client
3116
+ * @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
3117
+ * @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
3118
+ * @param {string} args.filepath - File to act upon.
3119
+ * @param {string} [args.oid] - OID of the object in the object database to add to the index with the specified filepath.
3120
+ * @param {number} [args.mode = 100644] - The file mode to add the file to the index.
3121
+ * @param {boolean} [args.add] - Adds the specified file to the index if it does not yet exist in the index.
3122
+ * @param {boolean} [args.remove] - Remove the specified file from the index if it does not exist in the workspace anymore.
3123
+ * @param {boolean} [args.force] - Remove the specified file from the index, even if it still exists in the workspace.
3124
+ * @param {object} [args.cache] - a [cache](cache.md) object
3125
+ *
3126
+ * @returns {Promise<string | void>} Resolves successfully with the SHA-1 object id of the object written or updated in the index, or nothing if the file was removed.
3127
+ *
3128
+ * @example
3129
+ * await git.updateIndex({
3130
+ * fs,
3131
+ * dir: '/tutorial',
3132
+ * filepath: 'readme.md'
3133
+ * })
3134
+ *
3135
+ * @example
3136
+ * // Manually create a blob in the object database.
3137
+ * let oid = await git.writeBlob({
3138
+ * fs,
3139
+ * dir: '/tutorial',
3140
+ * blob: new Uint8Array([])
3141
+ * })
3142
+ *
3143
+ * // Write the object in the object database to the index.
3144
+ * await git.updateIndex({
3145
+ * fs,
3146
+ * dir: '/tutorial',
3147
+ * add: true,
3148
+ * filepath: 'readme.md',
3149
+ * oid
3150
+ * })
3151
+ */
3152
+ export function updateIndex({ fs: _fs, dir, gitdir, cache, filepath, oid, mode, add, remove, force, }: {
3153
+ fs: CallbackFsClient | PromiseFsClient;
3154
+ dir: string;
3155
+ gitdir?: string;
3156
+ filepath: string;
3157
+ oid?: string;
3158
+ mode?: number;
3159
+ add?: boolean;
3160
+ remove?: boolean;
3161
+ force?: boolean;
3162
+ cache?: any;
3163
+ }): Promise<string | void>;
3110
3164
  /**
3111
3165
  * Return the version number of isomorphic-git
3112
3166
  *
@@ -3735,13 +3789,13 @@ declare namespace InternalError {
3735
3789
  }
3736
3790
  declare class InvalidFilepathError extends BaseError {
3737
3791
  /**
3738
- * @param {'leading-slash'|'trailing-slash'} [reason]
3792
+ * @param {'leading-slash'|'trailing-slash'|'directory'} [reason]
3739
3793
  */
3740
- constructor(reason?: "leading-slash" | "trailing-slash" | undefined);
3794
+ constructor(reason?: "leading-slash" | "trailing-slash" | "directory" | undefined);
3741
3795
  code: "InvalidFilepathError";
3742
3796
  name: "InvalidFilepathError";
3743
3797
  data: {
3744
- reason: "leading-slash" | "trailing-slash" | undefined;
3798
+ reason: "leading-slash" | "trailing-slash" | "directory" | undefined;
3745
3799
  };
3746
3800
  }
3747
3801
  declare namespace InvalidFilepathError {