@kubohiroya/turbowarp-plus-editor-git 0.14.1 → 0.16.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.ja.md +7 -3
- package/README.md +7 -3
- package/dist/file-diff-view.d.ts +3 -1
- package/dist/filesystem.d.ts +2 -2
- package/dist/history-graph-view.d.ts +40 -0
- package/dist/history-graph.d.ts +44 -0
- package/dist/index.d.ts +27 -1
- package/dist/index.js +778 -32
- package/dist/local-operations.d.ts +36 -0
- package/dist/messages.d.ts +74 -0
- package/dist/{project-comparison-LAyP_beL.js → project-comparison-MU0qD2wo.js} +213 -16
- package/dist/project-source-diff.d.ts +2 -0
- package/dist/project-source-diff.js +2 -2
- package/dist/project-source-review.d.ts +43 -0
- package/dist/project-source-tabs.d.ts +35 -0
- package/dist/pull-request-panel.d.ts +6 -1
- package/dist/{repository-CtvWYPQ8.js → repository-BYno-5f8.js} +927 -5
- package/dist/repository.d.ts +47 -0
- package/dist/repository.js +1 -1
- package/dist/style.css +30 -3
- package/package.json +1 -1
- package/src/file-diff-view.tsx +7 -3
- package/src/filesystem.ts +3 -1
- package/src/history-graph-view.tsx +233 -0
- package/src/history-graph.ts +90 -0
- package/src/index.tsx +175 -15
- package/src/local-operations.ts +134 -0
- package/src/messages.ts +40 -2
- package/src/project-source-diff.tsx +2 -0
- package/src/project-source-review.tsx +127 -15
- package/src/project-source-tabs.tsx +118 -0
- package/src/pull-request-panel.tsx +26 -5
- package/src/repository.ts +160 -1
- package/src/style.css +30 -3
|
@@ -11822,6 +11822,132 @@ async function _writeObject({ fs, gitdir, type, object, format = "content", oid
|
|
|
11822
11822
|
});
|
|
11823
11823
|
return oid;
|
|
11824
11824
|
}
|
|
11825
|
+
function posixifyPathBuffer(buffer) {
|
|
11826
|
+
let idx;
|
|
11827
|
+
while (~(idx = buffer.indexOf(92))) buffer[idx] = 47;
|
|
11828
|
+
return buffer;
|
|
11829
|
+
}
|
|
11830
|
+
/**
|
|
11831
|
+
* Add a file to the git index (aka staging area)
|
|
11832
|
+
*
|
|
11833
|
+
* @param {object} args
|
|
11834
|
+
* @param {FsClient} args.fs - a file system implementation
|
|
11835
|
+
* @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
|
|
11836
|
+
* @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
11837
|
+
* @param {string|string[]} args.filepath - The path to the file to add to the index
|
|
11838
|
+
* @param {object} [args.cache] - a [cache](cache.md) object
|
|
11839
|
+
* @param {boolean} [args.force=false] - add to index even if matches gitignore. Think `git add --force`
|
|
11840
|
+
* @param {boolean} [args.parallel=false] - process each input file in parallel. Parallel processing will result in more memory consumption but less process time
|
|
11841
|
+
*
|
|
11842
|
+
* @returns {Promise<void>} Resolves successfully once the git index has been updated
|
|
11843
|
+
*
|
|
11844
|
+
* @example
|
|
11845
|
+
* await fs.promises.writeFile('/tutorial/README.md', `# TEST`)
|
|
11846
|
+
* await git.add({ fs, dir: '/tutorial', filepath: 'README.md' })
|
|
11847
|
+
* console.log('done')
|
|
11848
|
+
*
|
|
11849
|
+
*/
|
|
11850
|
+
async function add({ fs: _fs, dir, gitdir = join(dir, ".git"), filepath, cache = {}, force = false, parallel = true }) {
|
|
11851
|
+
try {
|
|
11852
|
+
assertParameter("fs", _fs);
|
|
11853
|
+
assertParameter("dir", dir);
|
|
11854
|
+
assertParameter("gitdir", gitdir);
|
|
11855
|
+
assertParameter("filepath", filepath);
|
|
11856
|
+
const fs = new FileSystem(_fs);
|
|
11857
|
+
const updatedGitdir = await discoverGitdir({
|
|
11858
|
+
fsp: fs,
|
|
11859
|
+
dotgit: gitdir
|
|
11860
|
+
});
|
|
11861
|
+
await GitIndexManager.acquire({
|
|
11862
|
+
fs,
|
|
11863
|
+
gitdir: updatedGitdir,
|
|
11864
|
+
cache
|
|
11865
|
+
}, async (index) => {
|
|
11866
|
+
const autocrlf = await (await GitConfigManager.get({
|
|
11867
|
+
fs,
|
|
11868
|
+
gitdir: updatedGitdir
|
|
11869
|
+
})).get("core.autocrlf");
|
|
11870
|
+
return addToIndex({
|
|
11871
|
+
dir,
|
|
11872
|
+
gitdir: updatedGitdir,
|
|
11873
|
+
fs,
|
|
11874
|
+
filepath,
|
|
11875
|
+
index,
|
|
11876
|
+
force,
|
|
11877
|
+
parallel,
|
|
11878
|
+
autocrlf
|
|
11879
|
+
});
|
|
11880
|
+
});
|
|
11881
|
+
} catch (err) {
|
|
11882
|
+
err.caller = "git.add";
|
|
11883
|
+
throw err;
|
|
11884
|
+
}
|
|
11885
|
+
}
|
|
11886
|
+
function isTracked(index, filepath) {
|
|
11887
|
+
if (index.has({ filepath })) return true;
|
|
11888
|
+
const prefix = `${filepath}/`;
|
|
11889
|
+
for (const entry of index.entriesMap.keys()) if (entry.startsWith(prefix)) return true;
|
|
11890
|
+
return false;
|
|
11891
|
+
}
|
|
11892
|
+
async function addToIndex({ dir, gitdir, fs, filepath, index, force, parallel, autocrlf }) {
|
|
11893
|
+
filepath = Array.isArray(filepath) ? filepath : [filepath];
|
|
11894
|
+
const promises = filepath.map(async (currentFilepath) => {
|
|
11895
|
+
if (!force && !isTracked(index, currentFilepath)) {
|
|
11896
|
+
if (await GitIgnoreManager.isIgnored({
|
|
11897
|
+
fs,
|
|
11898
|
+
dir,
|
|
11899
|
+
gitdir,
|
|
11900
|
+
filepath: currentFilepath
|
|
11901
|
+
})) return;
|
|
11902
|
+
}
|
|
11903
|
+
const stats = await fs.lstat(join(dir, currentFilepath));
|
|
11904
|
+
if (!stats) throw new NotFoundError(currentFilepath);
|
|
11905
|
+
if (stats.isDirectory()) {
|
|
11906
|
+
const children = await fs.readdir(join(dir, currentFilepath));
|
|
11907
|
+
if (parallel) {
|
|
11908
|
+
const promises = children.map((child) => addToIndex({
|
|
11909
|
+
dir,
|
|
11910
|
+
gitdir,
|
|
11911
|
+
fs,
|
|
11912
|
+
filepath: [join(currentFilepath, child)],
|
|
11913
|
+
index,
|
|
11914
|
+
force,
|
|
11915
|
+
parallel,
|
|
11916
|
+
autocrlf
|
|
11917
|
+
}));
|
|
11918
|
+
await Promise.all(promises);
|
|
11919
|
+
} else for (const child of children) await addToIndex({
|
|
11920
|
+
dir,
|
|
11921
|
+
gitdir,
|
|
11922
|
+
fs,
|
|
11923
|
+
filepath: [join(currentFilepath, child)],
|
|
11924
|
+
index,
|
|
11925
|
+
force,
|
|
11926
|
+
parallel,
|
|
11927
|
+
autocrlf
|
|
11928
|
+
});
|
|
11929
|
+
} else {
|
|
11930
|
+
const object = stats.isSymbolicLink() ? await fs.readlink(join(dir, currentFilepath)).then(posixifyPathBuffer) : await fs.read(join(dir, currentFilepath), { autocrlf });
|
|
11931
|
+
if (object === null) throw new NotFoundError(currentFilepath);
|
|
11932
|
+
const oid = await _writeObject({
|
|
11933
|
+
fs,
|
|
11934
|
+
gitdir,
|
|
11935
|
+
type: "blob",
|
|
11936
|
+
object
|
|
11937
|
+
});
|
|
11938
|
+
index.insert({
|
|
11939
|
+
filepath: currentFilepath,
|
|
11940
|
+
stats,
|
|
11941
|
+
oid
|
|
11942
|
+
});
|
|
11943
|
+
}
|
|
11944
|
+
});
|
|
11945
|
+
const settledPromises = await Promise.allSettled(promises);
|
|
11946
|
+
const rejectedPromises = settledPromises.filter((settle) => settle.status === "rejected").map((settle) => settle.reason);
|
|
11947
|
+
if (rejectedPromises.length > 1) throw new MultipleGitError(rejectedPromises);
|
|
11948
|
+
if (rejectedPromises.length === 1) throw rejectedPromises[0];
|
|
11949
|
+
return settledPromises.filter((settle) => settle.status === "fulfilled" && settle.value).map((settle) => settle.value);
|
|
11950
|
+
}
|
|
11825
11951
|
async function hasObjectLoose({ fs, gitdir, oid }) {
|
|
11826
11952
|
const source = `objects/${oid.slice(0, 2)}/${oid.slice(2)}`;
|
|
11827
11953
|
return fs.exists(`${gitdir}/${source}`);
|
|
@@ -12248,6 +12374,141 @@ async function _resolveFilepath({ fs, cache, gitdir, tree, pathArray, oid, filep
|
|
|
12248
12374
|
throw new NotFoundError(`file or directory found at "${oid}:${filepath}"`);
|
|
12249
12375
|
}
|
|
12250
12376
|
/**
|
|
12377
|
+
*
|
|
12378
|
+
* @typedef {Object} ReadTreeResult - The object returned has the following schema:
|
|
12379
|
+
* @property {string} oid - SHA-1 object id of this tree
|
|
12380
|
+
* @property {TreeObject} tree - the parsed tree object
|
|
12381
|
+
*/
|
|
12382
|
+
/**
|
|
12383
|
+
* @param {object} args
|
|
12384
|
+
* @param {import('../models/FileSystem.js').FileSystem} args.fs
|
|
12385
|
+
* @param {any} args.cache
|
|
12386
|
+
* @param {string} args.gitdir
|
|
12387
|
+
* @param {string} args.oid
|
|
12388
|
+
* @param {string} [args.filepath]
|
|
12389
|
+
*
|
|
12390
|
+
* @returns {Promise<ReadTreeResult>}
|
|
12391
|
+
*/
|
|
12392
|
+
async function _readTree({ fs, cache, gitdir, oid, filepath = void 0 }) {
|
|
12393
|
+
if (filepath !== void 0) oid = await resolveFilepath({
|
|
12394
|
+
fs,
|
|
12395
|
+
cache,
|
|
12396
|
+
gitdir,
|
|
12397
|
+
oid,
|
|
12398
|
+
filepath
|
|
12399
|
+
});
|
|
12400
|
+
const { tree, oid: treeOid } = await resolveTree({
|
|
12401
|
+
fs,
|
|
12402
|
+
cache,
|
|
12403
|
+
gitdir,
|
|
12404
|
+
oid
|
|
12405
|
+
});
|
|
12406
|
+
return {
|
|
12407
|
+
oid: treeOid,
|
|
12408
|
+
tree: tree.entries()
|
|
12409
|
+
};
|
|
12410
|
+
}
|
|
12411
|
+
/**
|
|
12412
|
+
* Create a branch
|
|
12413
|
+
*
|
|
12414
|
+
* @param {object} args
|
|
12415
|
+
* @param {import('../models/FileSystem.js').FileSystem} args.fs
|
|
12416
|
+
* @param {string} args.gitdir
|
|
12417
|
+
* @param {string} args.ref
|
|
12418
|
+
* @param {string} [args.object = 'HEAD']
|
|
12419
|
+
* @param {boolean} [args.checkout = false]
|
|
12420
|
+
* @param {boolean} [args.force = false]
|
|
12421
|
+
*
|
|
12422
|
+
* @returns {Promise<void>} Resolves successfully when filesystem operations are complete
|
|
12423
|
+
*
|
|
12424
|
+
* @example
|
|
12425
|
+
* await git.branch({ dir: '$input((/))', ref: '$input((develop))' })
|
|
12426
|
+
* console.log('done')
|
|
12427
|
+
*
|
|
12428
|
+
*/
|
|
12429
|
+
async function _branch({ fs, gitdir, ref, object, checkout = false, force = false }) {
|
|
12430
|
+
if (!isValidRef(ref, true)) throw new InvalidRefNameError(ref, import_lib.default.clean(ref));
|
|
12431
|
+
const fullref = `refs/heads/${ref}`;
|
|
12432
|
+
if (!force) {
|
|
12433
|
+
if (await GitRefManager.exists({
|
|
12434
|
+
fs,
|
|
12435
|
+
gitdir,
|
|
12436
|
+
ref: fullref
|
|
12437
|
+
})) throw new AlreadyExistsError("branch", ref, false);
|
|
12438
|
+
}
|
|
12439
|
+
let oid;
|
|
12440
|
+
try {
|
|
12441
|
+
oid = await GitRefManager.resolve({
|
|
12442
|
+
fs,
|
|
12443
|
+
gitdir,
|
|
12444
|
+
ref: object || "HEAD"
|
|
12445
|
+
});
|
|
12446
|
+
} catch (e) {
|
|
12447
|
+
if (!(e instanceof NotFoundError)) throw e;
|
|
12448
|
+
}
|
|
12449
|
+
if (oid) {
|
|
12450
|
+
oid = (await resolveCommit({
|
|
12451
|
+
fs,
|
|
12452
|
+
cache: {},
|
|
12453
|
+
gitdir,
|
|
12454
|
+
oid
|
|
12455
|
+
})).oid;
|
|
12456
|
+
await GitRefManager.writeRef({
|
|
12457
|
+
fs,
|
|
12458
|
+
gitdir,
|
|
12459
|
+
ref: fullref,
|
|
12460
|
+
value: oid
|
|
12461
|
+
});
|
|
12462
|
+
}
|
|
12463
|
+
if (checkout) await GitRefManager.writeSymbolicRef({
|
|
12464
|
+
fs,
|
|
12465
|
+
gitdir,
|
|
12466
|
+
ref: "HEAD",
|
|
12467
|
+
value: fullref
|
|
12468
|
+
});
|
|
12469
|
+
}
|
|
12470
|
+
/**
|
|
12471
|
+
* Create a branch
|
|
12472
|
+
*
|
|
12473
|
+
* @param {object} args
|
|
12474
|
+
* @param {FsClient} args.fs - a file system implementation
|
|
12475
|
+
* @param {string} [args.dir] - The [working tree](dir-vs-gitdir.md) directory path
|
|
12476
|
+
* @param {string} [args.gitdir=join(dir,'.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
12477
|
+
* @param {string} args.ref - What to name the branch
|
|
12478
|
+
* @param {string} [args.object = 'HEAD'] - What oid to use as the start point. Accepts a symbolic ref.
|
|
12479
|
+
* @param {boolean} [args.checkout = false] - Update `HEAD` to point at the newly created branch
|
|
12480
|
+
* @param {boolean} [args.force = false] - Instead of throwing an error if a branched named `ref` already exists, overwrite the existing branch.
|
|
12481
|
+
*
|
|
12482
|
+
* @returns {Promise<void>} Resolves successfully when filesystem operations are complete
|
|
12483
|
+
*
|
|
12484
|
+
* @example
|
|
12485
|
+
* await git.branch({ fs, dir: '/tutorial', ref: 'develop' })
|
|
12486
|
+
* console.log('done')
|
|
12487
|
+
*
|
|
12488
|
+
*/
|
|
12489
|
+
async function branch({ fs, dir, gitdir = join(dir, ".git"), ref, object, checkout = false, force = false }) {
|
|
12490
|
+
try {
|
|
12491
|
+
assertParameter("fs", fs);
|
|
12492
|
+
assertParameter("gitdir", gitdir);
|
|
12493
|
+
assertParameter("ref", ref);
|
|
12494
|
+
const fsp = new FileSystem(fs);
|
|
12495
|
+
return await _branch({
|
|
12496
|
+
fs: fsp,
|
|
12497
|
+
gitdir: await discoverGitdir({
|
|
12498
|
+
fsp,
|
|
12499
|
+
dotgit: gitdir
|
|
12500
|
+
}),
|
|
12501
|
+
ref,
|
|
12502
|
+
object,
|
|
12503
|
+
checkout,
|
|
12504
|
+
force
|
|
12505
|
+
});
|
|
12506
|
+
} catch (err) {
|
|
12507
|
+
err.caller = "git.branch";
|
|
12508
|
+
throw err;
|
|
12509
|
+
}
|
|
12510
|
+
}
|
|
12511
|
+
/**
|
|
12251
12512
|
* Refuse to materialize a working-tree entry whose parent path traverses a
|
|
12252
12513
|
* symbolic link. A leading component that is a symlink could otherwise redirect
|
|
12253
12514
|
* a write or mkdir outside the working tree. git applies the same check: it does
|
|
@@ -12741,6 +13002,97 @@ async function batchAllSettled(operationName, tasks, onProgress, batchSize) {
|
|
|
12741
13002
|
if (rejections.length > 0) throw new MultipleGitError(rejections);
|
|
12742
13003
|
return results;
|
|
12743
13004
|
}
|
|
13005
|
+
/**
|
|
13006
|
+
* Checkout a branch
|
|
13007
|
+
*
|
|
13008
|
+
* If the branch already exists it will check out that branch. Otherwise, it will create a new remote tracking branch set to track the remote branch of that name.
|
|
13009
|
+
*
|
|
13010
|
+
* @param {object} args
|
|
13011
|
+
* @param {FsClient} args.fs - a file system implementation
|
|
13012
|
+
* @param {ProgressCallback} [args.onProgress] - optional progress event callback
|
|
13013
|
+
* @param {PostCheckoutCallback} [args.onPostCheckout] - optional post-checkout hook callback
|
|
13014
|
+
* @param {string} args.dir - The [working tree](dir-vs-gitdir.md) directory path
|
|
13015
|
+
* @param {string} [args.gitdir=join(dir,'.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
13016
|
+
* @param {string} [args.ref = 'HEAD'] - Source to checkout files from
|
|
13017
|
+
* @param {string[]} [args.filepaths] - Limit the checkout to the given files and directories. If `ref` is not provided, restore them from the index.
|
|
13018
|
+
* @param {string} [args.remote = 'origin'] - Which remote repository to use
|
|
13019
|
+
* @param {boolean} [args.noCheckout = false] - If true, will update HEAD but won't update the working directory
|
|
13020
|
+
* @param {boolean} [args.noUpdateHead] - If true, will update the working directory but won't update HEAD. Defaults to `false` when `ref` is provided, and `true` if `ref` is not provided.
|
|
13021
|
+
* @param {boolean} [args.dryRun = false] - If true, simulates a checkout so you can test whether it would succeed.
|
|
13022
|
+
* @param {boolean} [args.force = false] - If true, conflicts will be ignored and files will be overwritten regardless of local changes.
|
|
13023
|
+
* @param {boolean} [args.track = true] - If false, will not set the remote branch tracking information. Defaults to true.
|
|
13024
|
+
* @param {object} [args.cache] - a [cache](cache.md) object
|
|
13025
|
+
* @param {boolean} [args.nonBlocking = false] - If true, will use non-blocking file system operations to allow for better performance in certain environments (For example, in Browsers)
|
|
13026
|
+
* @param {number} [args.batchSize = 100] - If args.nonBlocking is true, batchSize is the number of files to process at a time avoid blocking the executing thread. The default value of 100 is a good starting point.
|
|
13027
|
+
*
|
|
13028
|
+
* @returns {Promise<void>} Resolves successfully when filesystem operations are complete
|
|
13029
|
+
*
|
|
13030
|
+
* @example
|
|
13031
|
+
* // switch to the main branch
|
|
13032
|
+
* await git.checkout({
|
|
13033
|
+
* fs,
|
|
13034
|
+
* dir: '/tutorial',
|
|
13035
|
+
* ref: 'main'
|
|
13036
|
+
* })
|
|
13037
|
+
* console.log('done')
|
|
13038
|
+
*
|
|
13039
|
+
* @example
|
|
13040
|
+
* // restore the 'docs' and 'src/docs' folders from the index, overwriting any unstaged changes
|
|
13041
|
+
* await git.checkout({
|
|
13042
|
+
* fs,
|
|
13043
|
+
* dir: '/tutorial',
|
|
13044
|
+
* force: true,
|
|
13045
|
+
* filepaths: ['docs', 'src/docs']
|
|
13046
|
+
* })
|
|
13047
|
+
* console.log('done')
|
|
13048
|
+
*
|
|
13049
|
+
* @example
|
|
13050
|
+
* // restore the 'docs' and 'src/docs' folders to the way they are in the 'develop' branch, overwriting any changes
|
|
13051
|
+
* await git.checkout({
|
|
13052
|
+
* fs,
|
|
13053
|
+
* dir: '/tutorial',
|
|
13054
|
+
* ref: 'develop',
|
|
13055
|
+
* noUpdateHead: true,
|
|
13056
|
+
* force: true,
|
|
13057
|
+
* filepaths: ['docs', 'src/docs']
|
|
13058
|
+
* })
|
|
13059
|
+
* console.log('done')
|
|
13060
|
+
*/
|
|
13061
|
+
async function checkout({ fs, onProgress, onPostCheckout, dir, gitdir = join(dir, ".git"), remote = "origin", ref: _ref, filepaths, noCheckout = false, noUpdateHead = _ref === void 0, dryRun = false, force = false, track = true, cache = {}, nonBlocking = false, batchSize = 100 }) {
|
|
13062
|
+
try {
|
|
13063
|
+
assertParameter("fs", fs);
|
|
13064
|
+
assertParameter("dir", dir);
|
|
13065
|
+
assertParameter("gitdir", gitdir);
|
|
13066
|
+
const ref = _ref || "HEAD";
|
|
13067
|
+
const restoreFromIndex = _ref === void 0 && filepaths != null && filepaths.length > 0;
|
|
13068
|
+
const fsp = new FileSystem(fs);
|
|
13069
|
+
return await _checkout({
|
|
13070
|
+
fs: fsp,
|
|
13071
|
+
cache,
|
|
13072
|
+
onProgress,
|
|
13073
|
+
onPostCheckout,
|
|
13074
|
+
dir,
|
|
13075
|
+
gitdir: await discoverGitdir({
|
|
13076
|
+
fsp,
|
|
13077
|
+
dotgit: gitdir
|
|
13078
|
+
}),
|
|
13079
|
+
remote,
|
|
13080
|
+
ref,
|
|
13081
|
+
filepaths,
|
|
13082
|
+
noCheckout,
|
|
13083
|
+
noUpdateHead,
|
|
13084
|
+
dryRun,
|
|
13085
|
+
force,
|
|
13086
|
+
track,
|
|
13087
|
+
restoreFromIndex,
|
|
13088
|
+
nonBlocking,
|
|
13089
|
+
batchSize
|
|
13090
|
+
});
|
|
13091
|
+
} catch (err) {
|
|
13092
|
+
err.caller = "git.checkout";
|
|
13093
|
+
throw err;
|
|
13094
|
+
}
|
|
13095
|
+
}
|
|
12744
13096
|
var LINEBREAKS = /^.*(\r?\n|$)/gm;
|
|
12745
13097
|
function mergeFile({ branches, contents }) {
|
|
12746
13098
|
const ourName = branches[1];
|
|
@@ -14037,6 +14389,81 @@ async function _fetch({ fs, cache, http, onProgress, onMessage, onAuth, onAuthSu
|
|
|
14037
14389
|
return res;
|
|
14038
14390
|
}
|
|
14039
14391
|
/**
|
|
14392
|
+
* Create a new commit
|
|
14393
|
+
*
|
|
14394
|
+
* @param {Object} args
|
|
14395
|
+
* @param {FsClient} args.fs - a file system implementation
|
|
14396
|
+
* @param {SignCallback} [args.onSign] - a PGP signing implementation
|
|
14397
|
+
* @param {string} [args.dir] - The [working tree](dir-vs-gitdir.md) directory path
|
|
14398
|
+
* @param {string} [args.gitdir=join(dir,'.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
14399
|
+
* @param {string} [args.message] - The commit message to use. Required, unless `amend === true`
|
|
14400
|
+
* @param {Object} [args.author] - The details about the author.
|
|
14401
|
+
* @param {string} [args.author.name] - Default is `user.name` config.
|
|
14402
|
+
* @param {string} [args.author.email] - Default is `user.email` config.
|
|
14403
|
+
* @param {number} [args.author.timestamp=Math.floor(Date.now()/1000)] - Set the author timestamp field. This is the integer number of seconds since the Unix epoch (1970-01-01 00:00:00).
|
|
14404
|
+
* @param {number} [args.author.timezoneOffset] - Set the author timezone offset field. This is the difference, in minutes, from the current timezone to UTC. Default is `(new Date()).getTimezoneOffset()`.
|
|
14405
|
+
* @param {Object} [args.committer = author] - The details about the commit committer, in the same format as the author parameter. If not specified, the author details are used.
|
|
14406
|
+
* @param {string} [args.committer.name] - Default is `user.name` config.
|
|
14407
|
+
* @param {string} [args.committer.email] - Default is `user.email` config.
|
|
14408
|
+
* @param {number} [args.committer.timestamp=Math.floor(Date.now()/1000)] - Set the committer timestamp field. This is the integer number of seconds since the Unix epoch (1970-01-01 00:00:00).
|
|
14409
|
+
* @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()`.
|
|
14410
|
+
* @param {string} [args.signingKey] - Sign the tag object using this private PGP key.
|
|
14411
|
+
* @param {boolean} [args.amend = false] - If true, replaces the last commit pointed to by `ref` with a new commit.
|
|
14412
|
+
* @param {boolean} [args.dryRun = false] - If true, simulates making a commit so you can test whether it would succeed. Implies `noUpdateBranch`.
|
|
14413
|
+
* @param {boolean} [args.noUpdateBranch = false] - If true, does not update the branch pointer after creating the commit.
|
|
14414
|
+
* @param {boolean} [args.disallowEmpty = false] - If true, throws an error instead of creating a commit with no staged changes.
|
|
14415
|
+
* @param {string} [args.ref] - The fully expanded name of the branch to commit to. Default is the current branch pointed to by HEAD. (TODO: fix it so it can expand branch names without throwing if the branch doesn't exist yet.)
|
|
14416
|
+
* @param {string[]} [args.parent] - The SHA-1 object ids of the commits to use as parents. If not specified, the commit pointed to by `ref` is used.
|
|
14417
|
+
* @param {string} [args.tree] - The SHA-1 object id of the tree to use. If not specified, a new tree object is created from the current git index.
|
|
14418
|
+
* @param {object} [args.cache] - a [cache](cache.md) object
|
|
14419
|
+
*
|
|
14420
|
+
* @returns {Promise<string>} Resolves successfully with the SHA-1 object id of the newly created commit.
|
|
14421
|
+
*
|
|
14422
|
+
* @example
|
|
14423
|
+
* let sha = await git.commit({
|
|
14424
|
+
* fs,
|
|
14425
|
+
* dir: '/tutorial',
|
|
14426
|
+
* author: {
|
|
14427
|
+
* name: 'Mr. Test',
|
|
14428
|
+
* email: 'mrtest@example.com',
|
|
14429
|
+
* },
|
|
14430
|
+
* message: 'Added the a.txt file'
|
|
14431
|
+
* })
|
|
14432
|
+
* console.log(sha)
|
|
14433
|
+
*
|
|
14434
|
+
*/
|
|
14435
|
+
async function commit({ fs: _fs, onSign, dir, gitdir = join(dir, ".git"), message, author, committer, signingKey, amend = false, dryRun = false, noUpdateBranch = false, disallowEmpty = false, ref, parent, tree, cache = {} }) {
|
|
14436
|
+
try {
|
|
14437
|
+
assertParameter("fs", _fs);
|
|
14438
|
+
if (!amend) assertParameter("message", message);
|
|
14439
|
+
if (signingKey) assertParameter("onSign", onSign);
|
|
14440
|
+
const fs = new FileSystem(_fs);
|
|
14441
|
+
return await _commit({
|
|
14442
|
+
fs,
|
|
14443
|
+
cache,
|
|
14444
|
+
onSign,
|
|
14445
|
+
gitdir: await discoverGitdir({
|
|
14446
|
+
fsp: fs,
|
|
14447
|
+
dotgit: gitdir
|
|
14448
|
+
}),
|
|
14449
|
+
message,
|
|
14450
|
+
author,
|
|
14451
|
+
committer,
|
|
14452
|
+
signingKey,
|
|
14453
|
+
amend,
|
|
14454
|
+
dryRun,
|
|
14455
|
+
noUpdateBranch,
|
|
14456
|
+
disallowEmpty,
|
|
14457
|
+
ref,
|
|
14458
|
+
parent,
|
|
14459
|
+
tree
|
|
14460
|
+
});
|
|
14461
|
+
} catch (err) {
|
|
14462
|
+
err.caller = "git.commit";
|
|
14463
|
+
throw err;
|
|
14464
|
+
}
|
|
14465
|
+
}
|
|
14466
|
+
/**
|
|
14040
14467
|
* Get the name of the branch currently pointed to by .git/HEAD
|
|
14041
14468
|
*
|
|
14042
14469
|
* @param {Object} args
|
|
@@ -15795,6 +16222,192 @@ async function readCommit({ fs, dir, gitdir = join(dir, ".git"), oid, cache = {}
|
|
|
15795
16222
|
}
|
|
15796
16223
|
}
|
|
15797
16224
|
/**
|
|
16225
|
+
*
|
|
16226
|
+
* @typedef {Object} ReadTreeResult - The object returned has the following schema:
|
|
16227
|
+
* @property {string} oid - SHA-1 object id of this tree
|
|
16228
|
+
* @property {TreeObject} tree - the parsed tree object
|
|
16229
|
+
*/
|
|
16230
|
+
/**
|
|
16231
|
+
* Read a tree object directly
|
|
16232
|
+
*
|
|
16233
|
+
* @param {object} args
|
|
16234
|
+
* @param {FsClient} args.fs - a file system client
|
|
16235
|
+
* @param {string} [args.dir] - The [working tree](dir-vs-gitdir.md) directory path
|
|
16236
|
+
* @param {string} [args.gitdir=join(dir,'.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
16237
|
+
* @param {string} args.oid - The SHA-1 object id to get. Annotated tags and commits are peeled.
|
|
16238
|
+
* @param {string} [args.filepath] - Don't return the object with `oid` itself, but resolve `oid` to a tree and then return the tree object at that filepath.
|
|
16239
|
+
* @param {object} [args.cache] - a [cache](cache.md) object
|
|
16240
|
+
*
|
|
16241
|
+
* @returns {Promise<ReadTreeResult>} Resolves successfully with a git tree object
|
|
16242
|
+
* @see ReadTreeResult
|
|
16243
|
+
* @see TreeObject
|
|
16244
|
+
* @see TreeEntry
|
|
16245
|
+
*
|
|
16246
|
+
*/
|
|
16247
|
+
async function readTree({ fs, dir, gitdir = join(dir, ".git"), oid, filepath = void 0, cache = {} }) {
|
|
16248
|
+
try {
|
|
16249
|
+
assertParameter("fs", fs);
|
|
16250
|
+
assertParameter("gitdir", gitdir);
|
|
16251
|
+
assertParameter("oid", oid);
|
|
16252
|
+
const fsp = new FileSystem(fs);
|
|
16253
|
+
return await _readTree({
|
|
16254
|
+
fs: fsp,
|
|
16255
|
+
cache,
|
|
16256
|
+
gitdir: await discoverGitdir({
|
|
16257
|
+
fsp,
|
|
16258
|
+
dotgit: gitdir
|
|
16259
|
+
}),
|
|
16260
|
+
oid,
|
|
16261
|
+
filepath
|
|
16262
|
+
});
|
|
16263
|
+
} catch (err) {
|
|
16264
|
+
err.caller = "git.readTree";
|
|
16265
|
+
throw err;
|
|
16266
|
+
}
|
|
16267
|
+
}
|
|
16268
|
+
/**
|
|
16269
|
+
* Remove a file from the git index (aka staging area)
|
|
16270
|
+
*
|
|
16271
|
+
* Note that this does NOT delete the file in the working directory.
|
|
16272
|
+
*
|
|
16273
|
+
* @param {object} args
|
|
16274
|
+
* @param {FsClient} args.fs - a file system client
|
|
16275
|
+
* @param {string} [args.dir] - The [working tree](dir-vs-gitdir.md) directory path
|
|
16276
|
+
* @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
16277
|
+
* @param {string|string[]} args.filepath - The path to the file to remove from the index, or an array of paths
|
|
16278
|
+
* @param {object} [args.cache] - a [cache](cache.md) object
|
|
16279
|
+
*
|
|
16280
|
+
* @returns {Promise<void>} Resolves successfully once the git index has been updated
|
|
16281
|
+
*
|
|
16282
|
+
* @example
|
|
16283
|
+
* await git.remove({ fs, dir: '/tutorial', filepath: 'README.md' })
|
|
16284
|
+
* console.log('done')
|
|
16285
|
+
*
|
|
16286
|
+
* @example
|
|
16287
|
+
* // remove several files from the index with a single write of the index
|
|
16288
|
+
* await git.remove({ fs, dir: '/tutorial', filepath: ['README.md', 'docs/old.md'] })
|
|
16289
|
+
* console.log('done')
|
|
16290
|
+
*
|
|
16291
|
+
*/
|
|
16292
|
+
async function remove({ fs: _fs, dir, gitdir = join(dir, ".git"), filepath, cache = {} }) {
|
|
16293
|
+
try {
|
|
16294
|
+
assertParameter("fs", _fs);
|
|
16295
|
+
assertParameter("gitdir", gitdir);
|
|
16296
|
+
assertParameter("filepath", filepath);
|
|
16297
|
+
const fsp = new FileSystem(_fs);
|
|
16298
|
+
const updatedGitdir = await discoverGitdir({
|
|
16299
|
+
fsp,
|
|
16300
|
+
dotgit: gitdir
|
|
16301
|
+
});
|
|
16302
|
+
await GitIndexManager.acquire({
|
|
16303
|
+
fs: fsp,
|
|
16304
|
+
gitdir: updatedGitdir,
|
|
16305
|
+
cache
|
|
16306
|
+
}, async function(index) {
|
|
16307
|
+
const filepaths = Array.isArray(filepath) ? filepath : [filepath];
|
|
16308
|
+
for (const currentFilepath of filepaths) index.delete({ filepath: currentFilepath });
|
|
16309
|
+
});
|
|
16310
|
+
} catch (err) {
|
|
16311
|
+
err.caller = "git.remove";
|
|
16312
|
+
throw err;
|
|
16313
|
+
}
|
|
16314
|
+
}
|
|
16315
|
+
async function hashObject$1({ gitdir, type, object }) {
|
|
16316
|
+
return shasum(GitObject.wrap({
|
|
16317
|
+
type,
|
|
16318
|
+
object
|
|
16319
|
+
}));
|
|
16320
|
+
}
|
|
16321
|
+
/**
|
|
16322
|
+
* Reset a file in the git index (aka staging area)
|
|
16323
|
+
*
|
|
16324
|
+
* Note that this does NOT modify the file in the working directory.
|
|
16325
|
+
*
|
|
16326
|
+
* @param {object} args
|
|
16327
|
+
* @param {FsClient} args.fs - a file system client
|
|
16328
|
+
* @param {string} [args.dir] - The [working tree](dir-vs-gitdir.md) directory path
|
|
16329
|
+
* @param {string} [args.gitdir=join(dir, '.git')] - [required] The [git directory](dir-vs-gitdir.md) path
|
|
16330
|
+
* @param {string} args.filepath - The path to the file to reset in the index
|
|
16331
|
+
* @param {string} [args.ref = 'HEAD'] - A ref to the commit to use
|
|
16332
|
+
* @param {object} [args.cache] - a [cache](cache.md) object
|
|
16333
|
+
*
|
|
16334
|
+
* @returns {Promise<void>} Resolves successfully once the git index has been updated
|
|
16335
|
+
*
|
|
16336
|
+
* @example
|
|
16337
|
+
* await git.resetIndex({ fs, dir: '/tutorial', filepath: 'README.md' })
|
|
16338
|
+
* console.log('done')
|
|
16339
|
+
*
|
|
16340
|
+
*/
|
|
16341
|
+
async function resetIndex({ fs: _fs, dir, gitdir = join(dir, ".git"), filepath, ref, cache = {} }) {
|
|
16342
|
+
try {
|
|
16343
|
+
assertParameter("fs", _fs);
|
|
16344
|
+
assertParameter("gitdir", gitdir);
|
|
16345
|
+
assertParameter("filepath", filepath);
|
|
16346
|
+
const fs = new FileSystem(_fs);
|
|
16347
|
+
const updatedGitdir = await discoverGitdir({
|
|
16348
|
+
fsp: fs,
|
|
16349
|
+
dotgit: gitdir
|
|
16350
|
+
});
|
|
16351
|
+
let oid;
|
|
16352
|
+
let workdirOid;
|
|
16353
|
+
try {
|
|
16354
|
+
oid = await GitRefManager.resolve({
|
|
16355
|
+
fs,
|
|
16356
|
+
gitdir: updatedGitdir,
|
|
16357
|
+
ref: ref || "HEAD"
|
|
16358
|
+
});
|
|
16359
|
+
} catch (e) {
|
|
16360
|
+
if (ref || !(e instanceof NotFoundError)) throw e;
|
|
16361
|
+
}
|
|
16362
|
+
if (oid) try {
|
|
16363
|
+
oid = await resolveFilepath({
|
|
16364
|
+
fs,
|
|
16365
|
+
cache,
|
|
16366
|
+
gitdir: updatedGitdir,
|
|
16367
|
+
oid,
|
|
16368
|
+
filepath
|
|
16369
|
+
});
|
|
16370
|
+
} catch (e) {
|
|
16371
|
+
if (!(e instanceof NotFoundError) && !(e instanceof InvalidFilepathError) && !(e instanceof ObjectTypeError && e.data.filepath)) throw e;
|
|
16372
|
+
oid = null;
|
|
16373
|
+
}
|
|
16374
|
+
let stats = {
|
|
16375
|
+
ctime: /* @__PURE__ */ new Date(0),
|
|
16376
|
+
mtime: /* @__PURE__ */ new Date(0),
|
|
16377
|
+
dev: 0,
|
|
16378
|
+
ino: 0,
|
|
16379
|
+
mode: 0,
|
|
16380
|
+
uid: 0,
|
|
16381
|
+
gid: 0,
|
|
16382
|
+
size: 0
|
|
16383
|
+
};
|
|
16384
|
+
const object = dir && await fs.read(join(dir, filepath));
|
|
16385
|
+
if (object) {
|
|
16386
|
+
workdirOid = await hashObject$1({
|
|
16387
|
+
gitdir: updatedGitdir,
|
|
16388
|
+
type: "blob",
|
|
16389
|
+
object
|
|
16390
|
+
});
|
|
16391
|
+
if (oid === workdirOid) stats = await fs.lstat(join(dir, filepath));
|
|
16392
|
+
}
|
|
16393
|
+
await GitIndexManager.acquire({
|
|
16394
|
+
fs,
|
|
16395
|
+
gitdir: updatedGitdir,
|
|
16396
|
+
cache
|
|
16397
|
+
}, async function(index) {
|
|
16398
|
+
index.delete({ filepath });
|
|
16399
|
+
if (oid) index.insert({
|
|
16400
|
+
filepath,
|
|
16401
|
+
stats,
|
|
16402
|
+
oid
|
|
16403
|
+
});
|
|
16404
|
+
});
|
|
16405
|
+
} catch (err) {
|
|
16406
|
+
err.caller = "git.reset";
|
|
16407
|
+
throw err;
|
|
16408
|
+
}
|
|
16409
|
+
}
|
|
16410
|
+
/**
|
|
15798
16411
|
* Get the value of a symbolic ref or resolve a ref to its SHA-1 object id
|
|
15799
16412
|
*
|
|
15800
16413
|
* @param {object} args
|
|
@@ -16494,7 +17107,7 @@ async function readOptional(fs, path) {
|
|
|
16494
17107
|
}
|
|
16495
17108
|
var sameBytes = (left, right) => left === right || Boolean(left && right && left.length === right.length && left.every((byte, i) => byte === right[i]));
|
|
16496
17109
|
// src/mutation-fs.ts
|
|
16497
|
-
var missing = (error) => {
|
|
17110
|
+
var missing$1 = (error) => {
|
|
16498
17111
|
var _errorCode;
|
|
16499
17112
|
return ["ENOENT", "ENOTDIR"].includes((_errorCode = errorCode(error)) !== null && _errorCode !== void 0 ? _errorCode : "");
|
|
16500
17113
|
};
|
|
@@ -16538,7 +17151,7 @@ function createMutationFs(root, worktree) {
|
|
|
16538
17151
|
time: stat.mtimeMs
|
|
16539
17152
|
};
|
|
16540
17153
|
} catch (error) {
|
|
16541
|
-
if (missing(error)) return null;
|
|
17154
|
+
if (missing$1(error)) return null;
|
|
16542
17155
|
throw error;
|
|
16543
17156
|
}
|
|
16544
17157
|
}
|
|
@@ -16549,7 +17162,7 @@ function createMutationFs(root, worktree) {
|
|
|
16549
17162
|
try {
|
|
16550
17163
|
return (await base.promises.stat(path)).isDirectory() ? "directory" : "file";
|
|
16551
17164
|
} catch (error) {
|
|
16552
|
-
if (!missing(error)) throw error;
|
|
17165
|
+
if (!missing$1(error)) throw error;
|
|
16553
17166
|
}
|
|
16554
17167
|
}
|
|
16555
17168
|
async function stage(path, after) {
|
|
@@ -16570,7 +17183,7 @@ function createMutationFs(root, worktree) {
|
|
|
16570
17183
|
try {
|
|
16571
17184
|
for (const entry of await base.promises.readdir(path)) names.add(entry);
|
|
16572
17185
|
} catch (error) {
|
|
16573
|
-
if (!missing(error)) throw error;
|
|
17186
|
+
if (!missing$1(error)) throw error;
|
|
16574
17187
|
}
|
|
16575
17188
|
for (const change of changes.values()) {
|
|
16576
17189
|
if (parent(change.path) !== path) continue;
|
|
@@ -16824,8 +17437,150 @@ async function runRemoteOperation(root, operation, getSession, guard, signal) {
|
|
|
16824
17437
|
};
|
|
16825
17438
|
return ((_globalThis$navigator = globalThis.navigator) === null || _globalThis$navigator === void 0 ? void 0 : _globalThis$navigator.locks) ? navigator.locks.request("tw-plus-git-mutation", execute) : execute();
|
|
16826
17439
|
}
|
|
17440
|
+
// src/local-operations.ts
|
|
17441
|
+
init_objectSpread2();
|
|
17442
|
+
var missing = (error) => {
|
|
17443
|
+
var _errorCode;
|
|
17444
|
+
return [
|
|
17445
|
+
"ENOENT",
|
|
17446
|
+
"ENOTDIR",
|
|
17447
|
+
"NotFoundError"
|
|
17448
|
+
].includes((_errorCode = errorCode(error)) !== null && _errorCode !== void 0 ? _errorCode : "");
|
|
17449
|
+
};
|
|
17450
|
+
/**
|
|
17451
|
+
* Stages, unstages or commits in the granted repository. Like pull, Git's writes are collected first and applied
|
|
17452
|
+
* after the files they replace are checked again; a failure part way puts back what was applied. Only `.git/index`,
|
|
17453
|
+
* objects and the current branch ref are written. The work tree and `.git/config` are never written.
|
|
17454
|
+
*/
|
|
17455
|
+
async function runLocalOperation(root, operation, guard) {
|
|
17456
|
+
var _globalThis$navigator;
|
|
17457
|
+
const execute = async () => {
|
|
17458
|
+
guard();
|
|
17459
|
+
const switching = operation.kind === "switchBranch" || operation.kind === "createBranch" && operation.checkout;
|
|
17460
|
+
const transaction = createMutationFs(root, switching);
|
|
17461
|
+
const options = {
|
|
17462
|
+
fs: transaction.fs,
|
|
17463
|
+
dir: "/",
|
|
17464
|
+
gitdir: "/.git",
|
|
17465
|
+
cache: {}
|
|
17466
|
+
};
|
|
17467
|
+
const read = createReadOnlyFs(root).promises;
|
|
17468
|
+
const exists = async (path) => {
|
|
17469
|
+
try {
|
|
17470
|
+
await read.stat(path);
|
|
17471
|
+
return true;
|
|
17472
|
+
} catch (error) {
|
|
17473
|
+
if (missing(error)) return false;
|
|
17474
|
+
throw error;
|
|
17475
|
+
}
|
|
17476
|
+
};
|
|
17477
|
+
for (const path of [
|
|
17478
|
+
"/.git/index.lock",
|
|
17479
|
+
"/.git/MERGE_HEAD",
|
|
17480
|
+
"/.git/CHERRY_PICK_HEAD",
|
|
17481
|
+
"/.git/REVERT_HEAD",
|
|
17482
|
+
"/.git/rebase-merge",
|
|
17483
|
+
"/.git/rebase-apply"
|
|
17484
|
+
]) if (await exists(path)) throw new GitPanelError("locked");
|
|
17485
|
+
if (operation.kind === "createBranch" || operation.kind === "switchBranch") {
|
|
17486
|
+
const name = operation.name.trim();
|
|
17487
|
+
const local = `refs/heads/${name}`;
|
|
17488
|
+
if (switching) {
|
|
17489
|
+
if ((await statusMatrix(_objectSpread2(_objectSpread2({}, options), {}, { refresh: false }))).some(([, head, worktree, stage]) => head !== worktree || head !== stage)) throw new GitPanelError("switchUnsafe");
|
|
17490
|
+
}
|
|
17491
|
+
if (operation.kind === "createBranch") {
|
|
17492
|
+
let object;
|
|
17493
|
+
if (operation.at !== void 0) {
|
|
17494
|
+
if (operation.fromRemote || !/^[0-9a-f]{40}$/.test(operation.at)) throw new GitPanelError("invalid");
|
|
17495
|
+
object = await readCommit(_objectSpread2(_objectSpread2({}, options), {}, { oid: operation.at })).then(() => operation.at, () => {
|
|
17496
|
+
throw new GitPanelError("invalid");
|
|
17497
|
+
});
|
|
17498
|
+
} else if (operation.fromRemote) try {
|
|
17499
|
+
object = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: `refs/remotes/origin/${name}` }));
|
|
17500
|
+
} catch (_unused) {
|
|
17501
|
+
throw new GitPanelError("invalid");
|
|
17502
|
+
}
|
|
17503
|
+
else object = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: "HEAD" })).catch(() => {
|
|
17504
|
+
throw new GitPanelError("unsupported");
|
|
17505
|
+
});
|
|
17506
|
+
try {
|
|
17507
|
+
await branch(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17508
|
+
ref: name,
|
|
17509
|
+
object,
|
|
17510
|
+
checkout: false
|
|
17511
|
+
}));
|
|
17512
|
+
} catch (error) {
|
|
17513
|
+
const code = error === null || error === void 0 ? void 0 : error.code;
|
|
17514
|
+
if (code === "AlreadyExistsError") throw new GitPanelError("branchExists");
|
|
17515
|
+
if (code === "InvalidRefNameError") throw new GitPanelError("branchName");
|
|
17516
|
+
throw error;
|
|
17517
|
+
}
|
|
17518
|
+
} else if (!(await listBranches(options)).includes(name)) throw new GitPanelError("invalid");
|
|
17519
|
+
if (switching) {
|
|
17520
|
+
const commit = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref: local }));
|
|
17521
|
+
await walk(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17522
|
+
trees: [TREE({ ref: commit })],
|
|
17523
|
+
map: async (path, [entry]) => {
|
|
17524
|
+
if (!entry || path === ".") return true;
|
|
17525
|
+
const type = await entry.type();
|
|
17526
|
+
if (type === "tree") return true;
|
|
17527
|
+
if (type !== "blob" || await entry.mode() !== 33188) throw new GitPanelError("unsupported");
|
|
17528
|
+
return true;
|
|
17529
|
+
}
|
|
17530
|
+
}));
|
|
17531
|
+
await checkout(_objectSpread2(_objectSpread2({}, options), {}, { ref: name }));
|
|
17532
|
+
if (await currentBranch(options) !== name) throw new GitPanelError("changed");
|
|
17533
|
+
}
|
|
17534
|
+
} else if (operation.kind === "stage") {
|
|
17535
|
+
const modes = /* @__PURE__ */ new Map();
|
|
17536
|
+
await walk(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17537
|
+
trees: [STAGE()],
|
|
17538
|
+
map: async (path, [entry]) => {
|
|
17539
|
+
if (entry && path !== "." && await entry.type() === "blob") modes.set(path, await entry.mode());
|
|
17540
|
+
return true;
|
|
17541
|
+
}
|
|
17542
|
+
}));
|
|
17543
|
+
for (const path of operation.paths) {
|
|
17544
|
+
pathParts(path);
|
|
17545
|
+
const mode = modes.get(path);
|
|
17546
|
+
if (mode !== void 0 && mode !== 33188) throw new GitPanelError("unsupported");
|
|
17547
|
+
if (await exists("/" + path)) await add(_objectSpread2(_objectSpread2({}, options), {}, { filepath: path }));
|
|
17548
|
+
else await remove(_objectSpread2(_objectSpread2({}, options), {}, { filepath: path }));
|
|
17549
|
+
}
|
|
17550
|
+
} else if (operation.kind === "unstage") for (const path of operation.paths) {
|
|
17551
|
+
pathParts(path);
|
|
17552
|
+
await resetIndex(_objectSpread2(_objectSpread2({}, options), {}, { filepath: path }));
|
|
17553
|
+
}
|
|
17554
|
+
else {
|
|
17555
|
+
var _await$configured, _operation$author, _await$configured2, _operation$author2;
|
|
17556
|
+
const message = operation.message.trim();
|
|
17557
|
+
if (!message) throw new GitPanelError("commitMessage");
|
|
17558
|
+
if (!await currentBranch(options)) throw new GitPanelError("unsupported");
|
|
17559
|
+
const configured = async (path) => {
|
|
17560
|
+
const value = await getConfig(_objectSpread2(_objectSpread2({}, options), {}, { path }));
|
|
17561
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
17562
|
+
};
|
|
17563
|
+
const name = (_await$configured = await configured("user.name")) !== null && _await$configured !== void 0 ? _await$configured : (_operation$author = operation.author) === null || _operation$author === void 0 || (_operation$author = _operation$author.name) === null || _operation$author === void 0 ? void 0 : _operation$author.trim();
|
|
17564
|
+
const email = (_await$configured2 = await configured("user.email")) !== null && _await$configured2 !== void 0 ? _await$configured2 : (_operation$author2 = operation.author) === null || _operation$author2 === void 0 || (_operation$author2 = _operation$author2.email) === null || _operation$author2 === void 0 ? void 0 : _operation$author2.trim();
|
|
17565
|
+
if (!name || !email) throw new GitPanelError("author");
|
|
17566
|
+
if (!(await statusMatrix(_objectSpread2(_objectSpread2({}, options), {}, { refresh: false }))).some(([, head, , stage]) => head !== stage)) throw new GitPanelError("nothingStaged");
|
|
17567
|
+
await commit(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17568
|
+
message: message + "\n",
|
|
17569
|
+
author: {
|
|
17570
|
+
name,
|
|
17571
|
+
email
|
|
17572
|
+
}
|
|
17573
|
+
}));
|
|
17574
|
+
}
|
|
17575
|
+
guard();
|
|
17576
|
+
await transaction.commit(guard);
|
|
17577
|
+
};
|
|
17578
|
+
return ((_globalThis$navigator = globalThis.navigator) === null || _globalThis$navigator === void 0 ? void 0 : _globalThis$navigator.locks) ? navigator.locks.request("tw-plus-git-mutation", execute) : execute();
|
|
17579
|
+
}
|
|
16827
17580
|
// src/repository.ts
|
|
16828
17581
|
init_objectSpread2();
|
|
17582
|
+
/** Commits read per history page unless asked otherwise. */
|
|
17583
|
+
var historyPageSize = 50;
|
|
16829
17584
|
var projectSourceLimit = 16777216;
|
|
16830
17585
|
var emptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
16831
17586
|
var textLimit = 1048576;
|
|
@@ -17089,6 +17844,7 @@ async function openRepository(root) {
|
|
|
17089
17844
|
}
|
|
17090
17845
|
const branch = await currentBranch(options);
|
|
17091
17846
|
const branches = await listBranches(options);
|
|
17847
|
+
const remoteBranches = (await listBranches(_objectSpread2(_objectSpread2({}, options), {}, { remote: "origin" }))).filter((name) => name !== "HEAD" && !branches.includes(name)).sort();
|
|
17092
17848
|
const commits = head ? (await log(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17093
17849
|
cache,
|
|
17094
17850
|
ref: head,
|
|
@@ -17144,6 +17900,7 @@ async function openRepository(root) {
|
|
|
17144
17900
|
branch: branch || void 0,
|
|
17145
17901
|
head,
|
|
17146
17902
|
branches,
|
|
17903
|
+
remoteBranches,
|
|
17147
17904
|
commits,
|
|
17148
17905
|
changes,
|
|
17149
17906
|
scope,
|
|
@@ -17152,6 +17909,147 @@ async function openRepository(root) {
|
|
|
17152
17909
|
upstream
|
|
17153
17910
|
};
|
|
17154
17911
|
};
|
|
17912
|
+
const walks = /* @__PURE__ */ new WeakMap();
|
|
17913
|
+
/**
|
|
17914
|
+
* Reads commits reachable from the local branches, `origin/*` and HEAD, newest committer time first, like
|
|
17915
|
+
* `git log --all --date-order` limited to those refs. A commit is listed only after every child read so far, and
|
|
17916
|
+
* only once no commit still to be read is newer, keeping a page's worth read ahead, so a child stays above its
|
|
17917
|
+
* parents unless committer times run backwards by more than that.
|
|
17918
|
+
*/
|
|
17919
|
+
const history = async ({ limit = 50, after } = {}) => {
|
|
17920
|
+
fs.clearReads();
|
|
17921
|
+
if (!(limit >= 1)) limit = 50;
|
|
17922
|
+
let walk = after ? walks.get(after) : void 0;
|
|
17923
|
+
if (after && !walk) throw new GitPanelError("changed");
|
|
17924
|
+
if (after) walks.delete(after);
|
|
17925
|
+
const readNode = async (oid) => {
|
|
17926
|
+
try {
|
|
17927
|
+
const { commit } = await readCommit(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
17928
|
+
cache,
|
|
17929
|
+
oid
|
|
17930
|
+
}));
|
|
17931
|
+
return {
|
|
17932
|
+
commit: {
|
|
17933
|
+
oid,
|
|
17934
|
+
parents: commit.parent,
|
|
17935
|
+
message: commit.message.split("\n")[0],
|
|
17936
|
+
author: commit.author.name,
|
|
17937
|
+
timestamp: commit.author.timestamp,
|
|
17938
|
+
refs: []
|
|
17939
|
+
},
|
|
17940
|
+
time: commit.committer.timestamp,
|
|
17941
|
+
children: 0,
|
|
17942
|
+
expanded: false
|
|
17943
|
+
};
|
|
17944
|
+
} catch (error) {
|
|
17945
|
+
if (errorCode(error) === "NotFoundError") return null;
|
|
17946
|
+
throw error;
|
|
17947
|
+
}
|
|
17948
|
+
};
|
|
17949
|
+
if (!walk) {
|
|
17950
|
+
walk = {
|
|
17951
|
+
nodes: /* @__PURE__ */ new Map(),
|
|
17952
|
+
pending: /* @__PURE__ */ new Set(),
|
|
17953
|
+
frontier: /* @__PURE__ */ new Set()
|
|
17954
|
+
};
|
|
17955
|
+
const tips = [];
|
|
17956
|
+
const branch = await currentBranch(options);
|
|
17957
|
+
for (const name of (await listBranches(options)).sort()) tips.push({
|
|
17958
|
+
ref: `refs/heads/${name}`,
|
|
17959
|
+
label: {
|
|
17960
|
+
kind: "branch",
|
|
17961
|
+
name,
|
|
17962
|
+
current: name === branch
|
|
17963
|
+
}
|
|
17964
|
+
});
|
|
17965
|
+
for (const name of (await listBranches(_objectSpread2(_objectSpread2({}, options), {}, { remote: "origin" }))).sort()) if (name !== "HEAD") tips.push({
|
|
17966
|
+
ref: `refs/remotes/origin/${name}`,
|
|
17967
|
+
label: {
|
|
17968
|
+
kind: "remote",
|
|
17969
|
+
name: `origin/${name}`,
|
|
17970
|
+
current: false
|
|
17971
|
+
}
|
|
17972
|
+
});
|
|
17973
|
+
if (!branch) tips.push({
|
|
17974
|
+
ref: "HEAD",
|
|
17975
|
+
label: {
|
|
17976
|
+
kind: "head",
|
|
17977
|
+
name: "HEAD",
|
|
17978
|
+
current: true
|
|
17979
|
+
}
|
|
17980
|
+
});
|
|
17981
|
+
for (const { ref, label } of tips) {
|
|
17982
|
+
let oid;
|
|
17983
|
+
try {
|
|
17984
|
+
oid = await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref }));
|
|
17985
|
+
} catch (error) {
|
|
17986
|
+
if (errorCode(error) === "NotFoundError") continue;
|
|
17987
|
+
throw error;
|
|
17988
|
+
}
|
|
17989
|
+
let node = walk.nodes.get(oid);
|
|
17990
|
+
if (!node) {
|
|
17991
|
+
const read = await readNode(oid);
|
|
17992
|
+
if (!read) continue;
|
|
17993
|
+
node = read;
|
|
17994
|
+
walk.nodes.set(oid, node);
|
|
17995
|
+
walk.pending.add(node);
|
|
17996
|
+
walk.frontier.add(node);
|
|
17997
|
+
}
|
|
17998
|
+
node.commit.refs.push(label);
|
|
17999
|
+
}
|
|
18000
|
+
}
|
|
18001
|
+
const { nodes, pending, frontier } = walk;
|
|
18002
|
+
const expand = async (node) => {
|
|
18003
|
+
frontier.delete(node);
|
|
18004
|
+
node.expanded = true;
|
|
18005
|
+
for (const parent of new Set(node.commit.parents)) {
|
|
18006
|
+
let next = nodes.get(parent);
|
|
18007
|
+
if (!next) {
|
|
18008
|
+
const read = await readNode(parent);
|
|
18009
|
+
if (!read) continue;
|
|
18010
|
+
next = read;
|
|
18011
|
+
nodes.set(parent, next);
|
|
18012
|
+
pending.add(next);
|
|
18013
|
+
frontier.add(next);
|
|
18014
|
+
}
|
|
18015
|
+
if (pending.has(node) && pending.has(next)) next.children++;
|
|
18016
|
+
}
|
|
18017
|
+
};
|
|
18018
|
+
const newer = (a, b) => a.time !== b.time ? a.time > b.time : a.commit.oid > b.commit.oid;
|
|
18019
|
+
const pick = (items, accept) => {
|
|
18020
|
+
let best = null;
|
|
18021
|
+
for (const node of items) if (accept(node) && (!best || newer(node, best))) best = node;
|
|
18022
|
+
return best;
|
|
18023
|
+
};
|
|
18024
|
+
const commits = [];
|
|
18025
|
+
while (commits.length < limit) {
|
|
18026
|
+
let ready = pick(pending, (node) => node.children === 0);
|
|
18027
|
+
let latest = pick(frontier, () => true);
|
|
18028
|
+
while (latest && (!ready || !ready.expanded || latest.time >= ready.time || pending.size < limit * 2)) {
|
|
18029
|
+
await expand(latest);
|
|
18030
|
+
ready = pick(pending, (node) => node.children === 0);
|
|
18031
|
+
latest = pick(frontier, () => true);
|
|
18032
|
+
}
|
|
18033
|
+
if (!ready) break;
|
|
18034
|
+
pending.delete(ready);
|
|
18035
|
+
for (const parent of new Set(ready.commit.parents)) {
|
|
18036
|
+
const node = nodes.get(parent);
|
|
18037
|
+
if (node && pending.has(node) && ready.expanded) node.children--;
|
|
18038
|
+
}
|
|
18039
|
+
commits.push(ready.commit);
|
|
18040
|
+
}
|
|
18041
|
+
await fs.checkReads();
|
|
18042
|
+
if (!pending.size) return {
|
|
18043
|
+
commits,
|
|
18044
|
+
cursor: null
|
|
18045
|
+
};
|
|
18046
|
+
const cursor = Object.freeze({ continues: "history" });
|
|
18047
|
+
walks.set(cursor, walk);
|
|
18048
|
+
return {
|
|
18049
|
+
commits,
|
|
18050
|
+
cursor
|
|
18051
|
+
};
|
|
18052
|
+
};
|
|
17155
18053
|
const historyChanges = async (snapshot, oid) => {
|
|
17156
18054
|
fs.clearReads();
|
|
17157
18055
|
await assertSnapshot(snapshot);
|
|
@@ -17437,10 +18335,34 @@ async function openRepository(root) {
|
|
|
17437
18335
|
throw error;
|
|
17438
18336
|
}
|
|
17439
18337
|
}),
|
|
18338
|
+
/** Stages, unstages or commits; see `runLocalOperation`. `guard` throws to stop before each write. */
|
|
18339
|
+
localOperation: (operation, guard) => serial(() => runLocalOperation(root, operation, guard)),
|
|
18340
|
+
/**
|
|
18341
|
+
* Whether a local branch, `origin/<name>` with `remote`, or the commit `name` with `remote` set to `'commit'`,
|
|
18342
|
+
* has the folder `path` (the root always exists).
|
|
18343
|
+
*/
|
|
18344
|
+
hasFolder: (name, path, remote = false) => serial(async () => {
|
|
18345
|
+
const ref = remote === "commit" ? name : remote ? `refs/remotes/origin/${name}` : `refs/heads/${name}`;
|
|
18346
|
+
if (!path) return true;
|
|
18347
|
+
try {
|
|
18348
|
+
const { tree } = await readTree(_objectSpread2(_objectSpread2({}, options), {}, {
|
|
18349
|
+
oid: await resolveRef(_objectSpread2(_objectSpread2({}, options), {}, { ref })),
|
|
18350
|
+
filepath: path
|
|
18351
|
+
}));
|
|
18352
|
+
return tree.length > 0;
|
|
18353
|
+
} catch (_unused5) {
|
|
18354
|
+
return false;
|
|
18355
|
+
}
|
|
18356
|
+
}),
|
|
17440
18357
|
read: (scope) => serial(() => read(scope)),
|
|
17441
18358
|
readDiff: (snapshot, change) => serial(() => readDiff(snapshot, change)),
|
|
17442
18359
|
historyChanges: (snapshot, oid) => serial(() => historyChanges(snapshot, oid)),
|
|
17443
18360
|
/**
|
|
18361
|
+
* Reads one page of the commits reachable from the local branches, `origin/*` and a detached HEAD, newest first
|
|
18362
|
+
* with each child above its parents, and the refs at each. Pass the returned `cursor` as `after` for the next page.
|
|
18363
|
+
*/
|
|
18364
|
+
history: (options) => serial(() => history(options)),
|
|
18365
|
+
/**
|
|
17444
18366
|
* Reads the assets of both versions of a selected project file, for thumbnails. Staged changes compare HEAD with
|
|
17445
18367
|
* the index, unstaged ones the index with the working tree, untracked ones nothing with the working tree, and
|
|
17446
18368
|
* commit changes the first parent with the commit. Each version's sb3-source.json names its assets directory.
|
|
@@ -17456,4 +18378,4 @@ async function openRepository(root) {
|
|
|
17456
18378
|
projectAssetLoader: (snapshot, folder, comparison) => ({ version, md5ext, signal }) => !isAssetName(md5ext) || signal.aborted ? Promise.resolve(null) : serial(() => readFolderAsset(snapshot, pathParts(folder), () => comparisonSource(snapshot, comparison, version), md5ext, signal)).catch(() => null)
|
|
17457
18379
|
};
|
|
17458
18380
|
}
|
|
17459
|
-
export { createReadOnlyFs as i,
|
|
18381
|
+
export { createReadOnlyFs as a, GitPanelError as i, openRepository as n, upstreamWalkLimit as r, historyPageSize as t };
|