isomorphic-git 1.21.0 → 1.22.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
@@ -16,6 +16,16 @@ By providing functionality as individual functions, code bundlers can produce sm
16
16
 
17
17
  The project includes type definitions so you can enjoy static type-checking and intelligent code completion in editors like VS Code and [CodeSandbox](https://codesandbox.io).
18
18
 
19
+ ## Project status
20
+ The original author of the project ([Billie Hilton](https://github.com/wmhilton)) left the project, but the project is still maintained by two volunteers:
21
+
22
+ * [@jcubic](https://github.com/jcubic) (most active)
23
+ * [@mojavelinux](https://github.com/mojavelinux)
24
+
25
+ But they don't write much code, mainly do code review and try to answer to issues and on Gitter, they just don't want the project to die. So you can say that this project is community driven (as jcubic always reply to issues). Which means that if you want a feature to be implemented you need to do this yourself or find someone that is willing to write the code for you. The project have some money on [OpenCollective](https://opencollective.com/isomorphic-git) and we can spend it on some development, if you find somoene that is willing to code in exchange to some bucks (it may be you), but we don't have a lot so don't expect to have full sallary.
26
+
27
+ If you want to help this project you're more than welcome to do so.
28
+
19
29
  ## Supported Environments
20
30
 
21
31
  The following environments are tested in CI and will continue to be supported until the next breaking version:
@@ -287,7 +297,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
287
297
  <td align="center"><a href="https://daniel-ruf.de"><img src="https://avatars1.githubusercontent.com/u/827205?v=4&s=60?s=60" width="60px;" alt=""/><br /><sub><b>Daniel Ruf</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=DanielRuf" title="Code">💻</a></td>
288
298
  <td align="center"><a href="http://blog.bokuweb.me/"><img src="https://avatars0.githubusercontent.com/u/10220449?v=4&s=60?s=60" width="60px;" alt=""/><br /><sub><b>bokuweb</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=bokuweb" title="Code">💻</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=bokuweb" title="Documentation">📖</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=bokuweb" title="Tests">⚠️</a></td>
289
299
  <td align="center"><a href="https://github.com/hirokiosame"><img src="https://avatars0.githubusercontent.com/u/1075694?v=4&s=60?s=60" width="60px;" alt=""/><br /><sub><b>Hiroki Osame</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=hirokiosame" title="Code">💻</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=hirokiosame" title="Documentation">📖</a></td>
290
- <td align="center"><a href="http://jcubic.pl/me"><img src="https://avatars1.githubusercontent.com/u/280241?v=4&s=60?s=60" width="60px;" alt=""/><br /><sub><b>Jakub Jankiewicz</b></sub></a><br /><a href="#question-jcubic" title="Answering Questions">💬</a> <a href="https://github.com/isomorphic-git/isomorphic-git/issues?q=author%3Ajcubic" title="Bug reports">🐛</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=jcubic" title="Code">💻</a> <a href="#example-jcubic" title="Examples">💡</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=jcubic" title="Tests">⚠️</a></td>
300
+ <td align="center"><a href="https://jcubic.pl/me"><img src="https://avatars1.githubusercontent.com/u/280241?v=4&s=60?s=60" width="60px;" alt=""/><br /><sub><b>Jakub Jankiewicz</b></sub></a><br /><a href="#question-jcubic" title="Answering Questions">💬</a> <a href="https://github.com/isomorphic-git/isomorphic-git/issues?q=author%3Ajcubic" title="Bug reports">🐛</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=jcubic" title="Code">💻</a> <a href="#example-jcubic" title="Examples">💡</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=jcubic" title="Tests">⚠️</a></td>
291
301
  </tr>
292
302
  <tr>
293
303
  <td align="center"><a href="https://github.com/howardgod"><img src="https://avatars1.githubusercontent.com/u/10459637?v=4&s=60?s=60" width="60px;" alt=""/><br /><sub><b>howardgod</b></sub></a><br /><a href="https://github.com/isomorphic-git/isomorphic-git/issues?q=author%3Ahowardgod" title="Bug reports">🐛</a> <a href="https://github.com/isomorphic-git/isomorphic-git/commits?author=howardgod" title="Code">💻</a></td>
@@ -1,10 +1,8 @@
1
1
  [
2
2
  "Chrome Headless 79.0.3945.0 (Linux x86_64)",
3
- "Firefox 103.0 (Ubuntu 0.0.0)",
3
+ "Firefox 110.0 (Ubuntu 0.0.0)",
4
+ "Chrome 108.0.0.0 (Android 10)",
4
5
  "Edge 79.0.309.65 (Windows 10)",
5
- "Mobile Safari 13.0 (iOS 13.0)",
6
- "X Chrome 103.0.5060.53 (Android 10)",
7
6
  "Safari 13.1 (Mac OS 10.15.4)",
8
- "X Chrome 103.0.5060.53 (Android 10)",
9
- "Chrome 103.0.5060.53 (Android 10)"
7
+ "Mobile Safari 13.0 (iOS 13.0)"
10
8
  ]
package/index.cjs CHANGED
@@ -342,6 +342,21 @@ class BaseError extends Error {
342
342
  }
343
343
  }
344
344
 
345
+ class UnmergedPathsError extends BaseError {
346
+ /**
347
+ * @param {Array<string>} filepaths
348
+ */
349
+ constructor(filepaths) {
350
+ super(
351
+ `Modifying the index is not possible because you have unmerged files: ${filepaths.toString}. Fix them up in the work tree, and then use 'git add/rm as appropriate to mark resolution and make a commit.`
352
+ );
353
+ this.code = this.name = UnmergedPathsError.code;
354
+ this.data = { filepaths };
355
+ }
356
+ }
357
+ /** @type {'UnmergedPathsError'} */
358
+ UnmergedPathsError.code = 'UnmergedPathsError';
359
+
345
360
  class InternalError extends BaseError {
346
361
  /**
347
362
  * @param {string} message
@@ -627,11 +642,28 @@ class GitIndex {
627
642
  _entries: Map<string, CacheEntry>
628
643
  _dirty: boolean // Used to determine if index needs to be saved to filesystem
629
644
  */
630
- constructor(entries) {
645
+ constructor(entries, unmergedPaths) {
631
646
  this._dirty = false;
647
+ this._unmergedPaths = unmergedPaths || new Set();
632
648
  this._entries = entries || new Map();
633
649
  }
634
650
 
651
+ _addEntry(entry) {
652
+ if (entry.flags.stage === 0) {
653
+ entry.stages = [entry];
654
+ this._entries.set(entry.path, entry);
655
+ this._unmergedPaths.delete(entry.path);
656
+ } else {
657
+ let existingEntry = this._entries.get(entry.path);
658
+ if (!existingEntry) {
659
+ this._entries.set(entry.path, entry);
660
+ existingEntry = entry;
661
+ }
662
+ existingEntry.stages[entry.flags.stage] = entry;
663
+ this._unmergedPaths.add(entry.path);
664
+ }
665
+ }
666
+
635
667
  static async from(buffer) {
636
668
  if (Buffer.isBuffer(buffer)) {
637
669
  return GitIndex.fromBuffer(buffer)
@@ -651,8 +683,8 @@ class GitIndex {
651
683
  `Invalid checksum in GitIndex buffer: expected ${shaClaimed} but saw ${shaComputed}`
652
684
  )
653
685
  }
686
+ const index = new GitIndex();
654
687
  const reader = new BufferCursor(buffer);
655
- const _entries = new Map();
656
688
  const magic = reader.toString('utf8', 4);
657
689
  if (magic !== 'DIRC') {
658
690
  throw new InternalError(`Inavlid dircache magic file number: ${magic}`)
@@ -707,10 +739,17 @@ class GitIndex {
707
739
  }
708
740
  }
709
741
  // end of awkward part
710
- _entries.set(entry.path, entry);
742
+ entry.stages = [];
743
+
744
+ index._addEntry(entry);
745
+
711
746
  i++;
712
747
  }
713
- return new GitIndex(_entries)
748
+ return index
749
+ }
750
+
751
+ get unmergedPaths() {
752
+ return [...this._unmergedPaths]
714
753
  }
715
754
 
716
755
  get entries() {
@@ -721,13 +760,33 @@ class GitIndex {
721
760
  return this._entries
722
761
  }
723
762
 
763
+ get entriesFlat() {
764
+ return [...this.entries].flatMap(entry => {
765
+ return entry.stages.length > 1 ? entry.stages.filter(x => x) : entry
766
+ })
767
+ }
768
+
724
769
  *[Symbol.iterator]() {
725
770
  for (const entry of this.entries) {
726
771
  yield entry;
727
772
  }
728
773
  }
729
774
 
730
- insert({ filepath, stats, oid }) {
775
+ insert({ filepath, stats, oid, stage = 0 }) {
776
+ if (!stats) {
777
+ stats = {
778
+ ctimeSeconds: 0,
779
+ ctimeNanoseconds: 0,
780
+ mtimeSeconds: 0,
781
+ mtimeNanoseconds: 0,
782
+ dev: 0,
783
+ ino: 0,
784
+ mode: 0,
785
+ uid: 0,
786
+ gid: 0,
787
+ size: 0,
788
+ };
789
+ }
731
790
  stats = normalizeStats(stats);
732
791
  const bfilepath = Buffer.from(filepath);
733
792
  const entry = {
@@ -749,11 +808,14 @@ class GitIndex {
749
808
  flags: {
750
809
  assumeValid: false,
751
810
  extended: false,
752
- stage: 0,
811
+ stage,
753
812
  nameLength: bfilepath.length < 0xfff ? bfilepath.length : 0xfff,
754
813
  },
814
+ stages: [],
755
815
  };
756
- this._entries.set(entry.path, entry);
816
+
817
+ this._addEntry(entry);
818
+
757
819
  this._dirty = true;
758
820
  }
759
821
 
@@ -767,6 +829,10 @@ class GitIndex {
767
829
  }
768
830
  }
769
831
  }
832
+
833
+ if (this._unmergedPaths.has(filepath)) {
834
+ this._unmergedPaths.delete(filepath);
835
+ }
770
836
  this._dirty = true;
771
837
  }
772
838
 
@@ -785,36 +851,50 @@ class GitIndex {
785
851
  .join('\n')
786
852
  }
787
853
 
854
+ static async _entryToBuffer(entry) {
855
+ const bpath = Buffer.from(entry.path);
856
+ // the fixed length + the filename + at least one null char => align by 8
857
+ const length = Math.ceil((62 + bpath.length + 1) / 8) * 8;
858
+ const written = Buffer.alloc(length);
859
+ const writer = new BufferCursor(written);
860
+ const stat = normalizeStats(entry);
861
+ writer.writeUInt32BE(stat.ctimeSeconds);
862
+ writer.writeUInt32BE(stat.ctimeNanoseconds);
863
+ writer.writeUInt32BE(stat.mtimeSeconds);
864
+ writer.writeUInt32BE(stat.mtimeNanoseconds);
865
+ writer.writeUInt32BE(stat.dev);
866
+ writer.writeUInt32BE(stat.ino);
867
+ writer.writeUInt32BE(stat.mode);
868
+ writer.writeUInt32BE(stat.uid);
869
+ writer.writeUInt32BE(stat.gid);
870
+ writer.writeUInt32BE(stat.size);
871
+ writer.write(entry.oid, 20, 'hex');
872
+ writer.writeUInt16BE(renderCacheEntryFlags(entry));
873
+ writer.write(entry.path, bpath.length, 'utf8');
874
+ return written
875
+ }
876
+
788
877
  async toObject() {
789
878
  const header = Buffer.alloc(12);
790
879
  const writer = new BufferCursor(header);
791
880
  writer.write('DIRC', 4, 'utf8');
792
881
  writer.writeUInt32BE(2);
793
- writer.writeUInt32BE(this.entries.length);
794
- const body = Buffer.concat(
795
- this.entries.map(entry => {
796
- const bpath = Buffer.from(entry.path);
797
- // the fixed length + the filename + at least one null char => align by 8
798
- const length = Math.ceil((62 + bpath.length + 1) / 8) * 8;
799
- const written = Buffer.alloc(length);
800
- const writer = new BufferCursor(written);
801
- const stat = normalizeStats(entry);
802
- writer.writeUInt32BE(stat.ctimeSeconds);
803
- writer.writeUInt32BE(stat.ctimeNanoseconds);
804
- writer.writeUInt32BE(stat.mtimeSeconds);
805
- writer.writeUInt32BE(stat.mtimeNanoseconds);
806
- writer.writeUInt32BE(stat.dev);
807
- writer.writeUInt32BE(stat.ino);
808
- writer.writeUInt32BE(stat.mode);
809
- writer.writeUInt32BE(stat.uid);
810
- writer.writeUInt32BE(stat.gid);
811
- writer.writeUInt32BE(stat.size);
812
- writer.write(entry.oid, 20, 'hex');
813
- writer.writeUInt16BE(renderCacheEntryFlags(entry));
814
- writer.write(entry.path, bpath.length, 'utf8');
815
- return written
816
- })
817
- );
882
+ writer.writeUInt32BE(this.entriesFlat.length);
883
+
884
+ let entryBuffers = [];
885
+ for (const entry of this.entries) {
886
+ entryBuffers.push(GitIndex._entryToBuffer(entry));
887
+ if (entry.stages.length > 1) {
888
+ for (const stage of entry.stages) {
889
+ if (stage && stage !== entry) {
890
+ entryBuffers.push(GitIndex._entryToBuffer(stage));
891
+ }
892
+ }
893
+ }
894
+ }
895
+ entryBuffers = await Promise.all(entryBuffers);
896
+
897
+ const body = Buffer.concat(entryBuffers);
818
898
  const main = Buffer.concat([header, body]);
819
899
  const sum = await shasum(main);
820
900
  return Buffer.concat([main, Buffer.from(sum, 'hex')])
@@ -880,14 +960,16 @@ class GitIndexManager {
880
960
  * @param {import('../models/FileSystem.js').FileSystem} opts.fs
881
961
  * @param {string} opts.gitdir
882
962
  * @param {object} opts.cache
963
+ * @param {bool} opts.allowUnmerged
883
964
  * @param {function(GitIndex): any} closure
884
965
  */
885
- static async acquire({ fs, gitdir, cache }, closure) {
966
+ static async acquire({ fs, gitdir, cache, allowUnmerged = true }, closure) {
886
967
  if (!cache[IndexCache]) cache[IndexCache] = createCache();
887
968
 
888
969
  const filepath = `${gitdir}/index`;
889
970
  if (lock === null) lock = new AsyncLock({ maxPending: Infinity });
890
971
  let result;
972
+ let unmergedPaths = [];
891
973
  await lock.acquire(filepath, async () => {
892
974
  // Acquire a file lock while we're reading the index
893
975
  // to make sure other processes aren't writing to it
@@ -897,6 +979,11 @@ class GitIndexManager {
897
979
  await updateCachedIndexFile(fs, filepath, cache[IndexCache]);
898
980
  }
899
981
  const index = cache[IndexCache].map.get(filepath);
982
+ unmergedPaths = index.unmergedPaths;
983
+
984
+ if (unmergedPaths.length && !allowUnmerged)
985
+ throw new UnmergedPathsError(unmergedPaths)
986
+
900
987
  result = await closure(index);
901
988
  if (index._dirty) {
902
989
  // Acquire a file lock while we're writing the index file
@@ -3415,7 +3502,8 @@ var Errors = /*#__PURE__*/Object.freeze({
3415
3502
  UnknownTransportError: UnknownTransportError,
3416
3503
  UnsafeFilepathError: UnsafeFilepathError,
3417
3504
  UrlParseError: UrlParseError,
3418
- UserCanceledError: UserCanceledError
3505
+ UserCanceledError: UserCanceledError,
3506
+ UnmergedPathsError: UnmergedPathsError
3419
3507
  });
3420
3508
 
3421
3509
  function formatAuthor({ name, email, timestamp, timezoneOffset }) {
@@ -4650,62 +4738,65 @@ async function _commit({
4650
4738
  });
4651
4739
  }
4652
4740
 
4653
- return GitIndexManager.acquire({ fs, gitdir, cache }, async function(index) {
4654
- const inodes = flatFileListToDirectoryStructure(index.entries);
4655
- const inode = inodes.get('.');
4656
- if (!tree) {
4657
- tree = await constructTree({ fs, gitdir, inode, dryRun });
4658
- }
4659
- if (!parent) {
4660
- try {
4661
- parent = [
4662
- await GitRefManager.resolve({
4663
- fs,
4664
- gitdir,
4665
- ref,
4666
- }),
4667
- ];
4668
- } catch (err) {
4669
- // Probably an initial commit
4670
- parent = [];
4741
+ return GitIndexManager.acquire(
4742
+ { fs, gitdir, cache, allowUnmerged: false },
4743
+ async function(index) {
4744
+ const inodes = flatFileListToDirectoryStructure(index.entries);
4745
+ const inode = inodes.get('.');
4746
+ if (!tree) {
4747
+ tree = await constructTree({ fs, gitdir, inode, dryRun });
4748
+ }
4749
+ if (!parent) {
4750
+ try {
4751
+ parent = [
4752
+ await GitRefManager.resolve({
4753
+ fs,
4754
+ gitdir,
4755
+ ref,
4756
+ }),
4757
+ ];
4758
+ } catch (err) {
4759
+ // Probably an initial commit
4760
+ parent = [];
4761
+ }
4762
+ } else {
4763
+ // ensure that the parents are oids, not refs
4764
+ parent = await Promise.all(
4765
+ parent.map(p => {
4766
+ return GitRefManager.resolve({ fs, gitdir, ref: p })
4767
+ })
4768
+ );
4671
4769
  }
4672
- } else {
4673
- // ensure that the parents are oids, not refs
4674
- parent = await Promise.all(
4675
- parent.map(p => {
4676
- return GitRefManager.resolve({ fs, gitdir, ref: p })
4677
- })
4678
- );
4679
- }
4680
4770
 
4681
- let comm = GitCommit.from({
4682
- tree,
4683
- parent,
4684
- author,
4685
- committer,
4686
- message,
4687
- });
4688
- if (signingKey) {
4689
- comm = await GitCommit.sign(comm, onSign, signingKey);
4690
- }
4691
- const oid = await _writeObject({
4692
- fs,
4693
- gitdir,
4694
- type: 'commit',
4695
- object: comm.toObject(),
4696
- dryRun,
4697
- });
4698
- if (!noUpdateBranch && !dryRun) {
4699
- // Update branch pointer
4700
- await GitRefManager.writeRef({
4771
+ let comm = GitCommit.from({
4772
+ tree,
4773
+ parent,
4774
+ author,
4775
+ committer,
4776
+ message,
4777
+ });
4778
+ if (signingKey) {
4779
+ comm = await GitCommit.sign(comm, onSign, signingKey);
4780
+ }
4781
+ const oid = await _writeObject({
4701
4782
  fs,
4702
4783
  gitdir,
4703
- ref,
4704
- value: oid,
4784
+ type: 'commit',
4785
+ object: comm.toObject(),
4786
+ dryRun,
4705
4787
  });
4788
+ if (!noUpdateBranch && !dryRun) {
4789
+ // Update branch pointer
4790
+ await GitRefManager.writeRef({
4791
+ fs,
4792
+ gitdir,
4793
+ ref,
4794
+ value: oid,
4795
+ });
4796
+ }
4797
+ return oid
4706
4798
  }
4707
- return oid
4708
- })
4799
+ )
4709
4800
  }
4710
4801
 
4711
4802
  async function constructTree({ fs, gitdir, inode, dryRun }) {
@@ -4788,7 +4879,7 @@ async function _resolveFilepath({
4788
4879
  oid: entry.oid,
4789
4880
  });
4790
4881
  if (type !== 'tree') {
4791
- throw new ObjectTypeError(oid, type, 'blob', filepath)
4882
+ throw new ObjectTypeError(oid, type, 'tree', filepath)
4792
4883
  }
4793
4884
  tree = GitTree.from(object);
4794
4885
  return _resolveFilepath({
@@ -7007,8 +7098,8 @@ function filterCapabilities(server, client) {
7007
7098
 
7008
7099
  const pkg = {
7009
7100
  name: 'isomorphic-git',
7010
- version: '1.21.0',
7011
- agent: 'git/isomorphic-git@1.21.0',
7101
+ version: '1.22.0',
7102
+ agent: 'git/isomorphic-git@1.22.0',
7012
7103
  };
7013
7104
 
7014
7105
  class FIFO {
@@ -8887,6 +8978,7 @@ async function _merge({
8887
8978
  oids: [ourOid, theirOid],
8888
8979
  });
8889
8980
  if (baseOids.length !== 1) {
8981
+ // TODO: Recursive Merge strategy
8890
8982
  throw new MergeNotSupportedError()
8891
8983
  }
8892
8984
  const baseOid = baseOids[0];
@@ -8911,21 +9003,32 @@ async function _merge({
8911
9003
  throw new FastForwardError()
8912
9004
  }
8913
9005
  // try a fancier merge
8914
- const tree = await mergeTree({
8915
- fs,
8916
- cache,
8917
- dir,
8918
- gitdir,
8919
- ourOid,
8920
- theirOid,
8921
- baseOid,
8922
- ourName: abbreviateRef(ours),
8923
- baseName: 'base',
8924
- theirName: abbreviateRef(theirs),
8925
- dryRun,
8926
- abortOnConflict,
8927
- mergeDriver,
8928
- });
9006
+ const tree = await GitIndexManager.acquire(
9007
+ { fs, gitdir, cache, allowUnmerged: false },
9008
+ async index => {
9009
+ return mergeTree({
9010
+ fs,
9011
+ cache,
9012
+ dir,
9013
+ gitdir,
9014
+ index,
9015
+ ourOid,
9016
+ theirOid,
9017
+ baseOid,
9018
+ ourName: abbreviateRef(ours),
9019
+ baseName: 'base',
9020
+ theirName: abbreviateRef(theirs),
9021
+ dryRun,
9022
+ abortOnConflict,
9023
+ mergeDriver,
9024
+ })
9025
+ }
9026
+ );
9027
+
9028
+ // Defer throwing error until the index lock is relinquished and index is
9029
+ // written to filsesystem
9030
+ if (tree instanceof MergeConflictError) throw tree
9031
+
8929
9032
  if (!message) {
8930
9033
  message = `Merge branch '${abbreviateRef(theirs)}' into ${abbreviateRef(
8931
9034
  ours
package/index.d.ts CHANGED
@@ -742,6 +742,7 @@ export var Errors: Readonly<{
742
742
  UnsafeFilepathError: typeof UnsafeFilepathError;
743
743
  UrlParseError: typeof UrlParseError;
744
744
  UserCanceledError: typeof UserCanceledError;
745
+ UnmergedPathsError: typeof UnmergedPathsError;
745
746
  }>;
746
747
  /**
747
748
  * @returns {Walker}
@@ -4176,6 +4177,21 @@ declare namespace UserCanceledError {
4176
4177
  const code_28: 'UserCanceledError';
4177
4178
  export { code_28 as code };
4178
4179
  }
4180
+ declare class UnmergedPathsError extends BaseError {
4181
+ /**
4182
+ * @param {Array<string>} filepaths
4183
+ */
4184
+ constructor(filepaths: string[]);
4185
+ code: "UnmergedPathsError";
4186
+ name: "UnmergedPathsError";
4187
+ data: {
4188
+ filepaths: string[];
4189
+ };
4190
+ }
4191
+ declare namespace UnmergedPathsError {
4192
+ const code_29: 'UnmergedPathsError';
4193
+ export { code_29 as code };
4194
+ }
4179
4195
  /**
4180
4196
  * @typedef {Object} GitProgressEvent
4181
4197
  * @property {string} phase