dsh-ide-git 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +1130 -29
- package/src/index.js +53 -19
package/src/index.js
CHANGED
|
@@ -948,17 +948,34 @@ async function branchRename(payload) {
|
|
|
948
948
|
async function branchDelete(payload) {
|
|
949
949
|
const cwd = cwdOf(payload)
|
|
950
950
|
const root = await repoRootOf(cwd, payload)
|
|
951
|
-
const name = requireRef(payload.name, 'name')
|
|
952
951
|
const force = payload.force === true
|
|
953
952
|
if (force && payload.confirm !== true) throw badRequest('force delete requires confirm: true')
|
|
953
|
+
/* Batch form (multi-select): `names` wins when present; the single `name`
|
|
954
|
+
keeps working exactly as before. Every name goes through the same ref
|
|
955
|
+
guard — an array never bypasses validation. */
|
|
956
|
+
const names = Array.isArray(payload.names)
|
|
957
|
+
? payload.names.map((entry) => requireRef(entry, 'name'))
|
|
958
|
+
: [requireRef(payload.name, 'name')]
|
|
954
959
|
const current = await currentBranchOf(root)
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
960
|
+
for (const name of names) {
|
|
961
|
+
if (name === current) throw new PanelError('protected-branch', 'refusing to delete the checked-out branch "' + name + '"', 409)
|
|
962
|
+
if (PROTECTED_BRANCHES.has(name) && payload.confirm !== true) throw badRequest('deleting "' + name + '" requires confirm: true')
|
|
963
|
+
}
|
|
964
|
+
const branches = []
|
|
965
|
+
for (const name of names) {
|
|
966
|
+
const head = (await git(root, ['rev-parse', '--verify', 'refs/heads/' + name])).trim()
|
|
967
|
+
branches.push({ name: name, hash: head })
|
|
968
|
+
}
|
|
958
969
|
// -d (the default) refuses to drop an unmerged branch: git is the guard here.
|
|
959
|
-
await git(root, ['branch', force ? '-D' : '-d',
|
|
960
|
-
const
|
|
961
|
-
|
|
970
|
+
await git(root, ['branch', force ? '-D' : '-d', ...names])
|
|
971
|
+
const label = names.length === 1 ? names[0] : names.length + ' branches'
|
|
972
|
+
const undo = pushUndo(root, {
|
|
973
|
+
kind: 'branch-delete', label: label,
|
|
974
|
+
branches: branches,
|
|
975
|
+
// Legacy single-branch fields stay so an in-flight old handle keeps working.
|
|
976
|
+
name: names[0], hash: branches[0].hash,
|
|
977
|
+
})
|
|
978
|
+
return { branch: names[0], hash: branches[0].hash, names: names, branches: branches, force: force, undo: undo }
|
|
962
979
|
}
|
|
963
980
|
|
|
964
981
|
async function merge(payload) {
|
|
@@ -1030,10 +1047,14 @@ async function branchDeleteRemote(payload) {
|
|
|
1030
1047
|
const cwd = cwdOf(payload)
|
|
1031
1048
|
const root = await repoRootOf(cwd, payload)
|
|
1032
1049
|
const remote = requireRef(payload.remote, 'remote')
|
|
1033
|
-
|
|
1050
|
+
/* Batch form (multi-select): `branches` wins; the single `branch` keeps
|
|
1051
|
+
working. `git push --delete` accepts several names in one go. */
|
|
1052
|
+
const branches = Array.isArray(payload.branches)
|
|
1053
|
+
? payload.branches.map((entry) => requireRef(entry, 'branch'))
|
|
1054
|
+
: [requireRef(payload.branch, 'branch')]
|
|
1034
1055
|
if (payload.confirm !== true) throw badRequest('deleting a remote branch requires confirm: true')
|
|
1035
|
-
const out = await git(root, ['push', remote, '--delete',
|
|
1036
|
-
return { remote, branch, output: out.trim() }
|
|
1056
|
+
const out = await git(root, ['push', remote, '--delete', ...branches], { timeoutMs: NETWORK_TIMEOUT_MS })
|
|
1057
|
+
return { remote: remote, branch: branches[0], branches: branches, output: out.trim() }
|
|
1037
1058
|
}
|
|
1038
1059
|
|
|
1039
1060
|
/* SCM ▸ Tags ▸ Delete Remote Tag... / Push Tags. */
|
|
@@ -1070,12 +1091,17 @@ async function stashClear(payload) {
|
|
|
1070
1091
|
async function cherryPick(payload) {
|
|
1071
1092
|
const cwd = cwdOf(payload)
|
|
1072
1093
|
const root = await repoRootOf(cwd, payload)
|
|
1073
|
-
|
|
1074
|
-
|
|
1094
|
+
/* Batch form (multi-select): `hashes` are applied in the order given — the
|
|
1095
|
+
client sends them oldest-first so the sequence replays topologically. The
|
|
1096
|
+
single `hash` keeps working. */
|
|
1097
|
+
const hashes = Array.isArray(payload.hashes)
|
|
1098
|
+
? payload.hashes.map((entry) => requireRef(entry, 'hash'))
|
|
1099
|
+
: [requireRef(payload.hash, 'hash')]
|
|
1100
|
+
const result = await runGit(root, ['cherry-pick', ...hashes])
|
|
1075
1101
|
const combined = (result.stdout + '\n' + result.stderr).trim()
|
|
1076
1102
|
const conflicted = /CONFLICT/.test(combined)
|
|
1077
1103
|
if (result.code !== 0 && !conflicted) throw new PanelError('git-failed', combined === '' ? 'cherry-pick failed' : combined, 409)
|
|
1078
|
-
return { hash, conflicted, output: combined }
|
|
1104
|
+
return { hash: hashes[0], hashes: hashes, conflicted: conflicted, output: combined }
|
|
1079
1105
|
}
|
|
1080
1106
|
|
|
1081
1107
|
async function revert(payload) {
|
|
@@ -1201,9 +1227,12 @@ async function tagCreate(payload) {
|
|
|
1201
1227
|
async function tagDelete(payload) {
|
|
1202
1228
|
const cwd = cwdOf(payload)
|
|
1203
1229
|
const root = await repoRootOf(cwd, payload)
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1230
|
+
/* Batch form (multi-select): `names` wins; the single `name` keeps working. */
|
|
1231
|
+
const names = Array.isArray(payload.names)
|
|
1232
|
+
? payload.names.map((entry) => requireRef(entry, 'name'))
|
|
1233
|
+
: [requireRef(payload.name, 'name')]
|
|
1234
|
+
await git(root, ['tag', '-d', ...names])
|
|
1235
|
+
return { tag: names[0], names: names }
|
|
1207
1236
|
}
|
|
1208
1237
|
|
|
1209
1238
|
/* VS Code's SCM title "Clone..." — clone a URL into an absolute local path.
|
|
@@ -1257,9 +1286,14 @@ async function undoApply(payload) {
|
|
|
1257
1286
|
if (index < 0) throw new PanelError('undo-gone', 'this action can no longer be undone', 409)
|
|
1258
1287
|
const entry = list[index]
|
|
1259
1288
|
if (entry.kind === 'branch-delete') {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1289
|
+
/* Batch handles carry `branches: [{name, hash}]`; older single-branch
|
|
1290
|
+
handles only have `name`/`hash` — normalise both to one list. */
|
|
1291
|
+
const restores = Array.isArray(entry.branches) ? entry.branches : [{ name: entry.name, hash: entry.hash }]
|
|
1292
|
+
for (const item of restores) {
|
|
1293
|
+
const exists = await runGit(root, ['show-ref', '--verify', '--quiet', 'refs/heads/' + item.name])
|
|
1294
|
+
if (exists.code === 0) throw new PanelError('undo-conflict', 'branch "' + item.name + '" already exists again', 409)
|
|
1295
|
+
await git(root, ['branch', item.name, item.hash])
|
|
1296
|
+
}
|
|
1263
1297
|
} else if (entry.kind === 'stash-drop') {
|
|
1264
1298
|
await git(root, ['stash', 'store', '-m', 'dsh-ide-git: restored ' + entry.label, entry.hash])
|
|
1265
1299
|
} else if (entry.kind === 'discard') {
|