dsh-ide-git 0.14.4 → 0.14.5
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 +23 -12
- package/src/index.js +42 -3
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ide-git",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/client.js
CHANGED
|
@@ -7980,13 +7980,19 @@ window.__ModuleLoader__.load({
|
|
|
7980
7980
|
setAiNote(null)
|
|
7981
7981
|
try {
|
|
7982
7982
|
const settings = props.commitSettings === undefined ? { model: '', reasoning: '', prompt: '' } : props.commitSettings
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7983
|
+
/* `base` carries the picked repository ({ cwd, repoRoot }) exactly like
|
|
7984
|
+
every other request: without it the host resolves the repository from
|
|
7985
|
+
the session cwd, which need not be one (several sibling repos). */
|
|
7986
|
+
const data = await props.request('commit-message', Object.assign(
|
|
7987
|
+
{},
|
|
7988
|
+
props.base === undefined ? { cwd: props.cwd } : props.base,
|
|
7989
|
+
{
|
|
7990
|
+
sessionId: props.sessionId,
|
|
7991
|
+
model: settings.model,
|
|
7992
|
+
reasoningEffort: settings.reasoning,
|
|
7993
|
+
prompt: settings.prompt,
|
|
7994
|
+
},
|
|
7995
|
+
))
|
|
7990
7996
|
setAiUndo(message === '' ? null : message)
|
|
7991
7997
|
setMessage(typeof data.message === 'string' ? data.message : '')
|
|
7992
7998
|
/* Say what the model did NOT see, instead of quietly sending less. */
|
|
@@ -8885,7 +8891,7 @@ window.__ModuleLoader__.load({
|
|
|
8885
8891
|
const scopeObject = scope || {}
|
|
8886
8892
|
previewStore.set(commitHash !== null
|
|
8887
8893
|
? { hash: commitHash, subject: seed.subject || '', path: seed.path !== undefined && seed.path !== null ? seed.path : '', cwd: scopeObject.cwd, repoRoot: repoRoot, sessionId: scopeObject.sessionId }
|
|
8888
|
-
: { path: seed.path, staged: seed.staged === true, cwd: scopeObject.cwd, repoRoot: repoRoot, sessionId: scopeObject.sessionId })
|
|
8894
|
+
: { path: seed.path, staged: seed.staged === true, untracked: seed.untracked === true ? true : undefined, cwd: scopeObject.cwd, repoRoot: repoRoot, sessionId: scopeObject.sessionId })
|
|
8889
8895
|
sidebarRight.openTab(NATIVE_PREVIEW_KIND)
|
|
8890
8896
|
return true
|
|
8891
8897
|
} catch (error) { void error }
|
|
@@ -9368,7 +9374,7 @@ window.__ModuleLoader__.load({
|
|
|
9368
9374
|
const commitSettings = readCommitSettings()
|
|
9369
9375
|
const changesPane = E(ChangesPanel, {
|
|
9370
9376
|
t: t, summary: summary, busy: busy, compact: compact, hideHeader: compact,
|
|
9371
|
-
request: request, cwd: cwd, sessionId: sessionId, commitSettings: commitSettings,
|
|
9377
|
+
request: request, cwd: cwd, sessionId: sessionId, commitSettings: commitSettings, base: base,
|
|
9372
9378
|
showIgnored: showIgnored,
|
|
9373
9379
|
onToggleIgnored: () => setShowIgnored((value) => !value),
|
|
9374
9380
|
// Commit-and-push carries the confirm flag with it: the button already
|
|
@@ -9447,7 +9453,7 @@ window.__ModuleLoader__.load({
|
|
|
9447
9453
|
['untracked', changes.untracked, false],
|
|
9448
9454
|
]
|
|
9449
9455
|
for (const entry of order) {
|
|
9450
|
-
if (entry[1].length > 0) { showDiff({ path: entry[1][0].path, staged: entry[2] }); return }
|
|
9456
|
+
if (entry[1].length > 0) { showDiff({ path: entry[1][0].path, staged: entry[2], untracked: entry[0] === 'untracked' }); return }
|
|
9451
9457
|
}
|
|
9452
9458
|
setNote(t('note.noChanges'))
|
|
9453
9459
|
}
|
|
@@ -10519,7 +10525,7 @@ window.__ModuleLoader__.load({
|
|
|
10519
10525
|
? ''
|
|
10520
10526
|
: (seed.hash !== undefined && seed.hash !== null
|
|
10521
10527
|
? 'c:' + String(seed.hash) + ':' + String(seed.path !== undefined && seed.path !== null ? seed.path : '')
|
|
10522
|
-
: 'w:' + String(seed.staged === true) + ':' + String(seed.path))
|
|
10528
|
+
: 'w:' + String(seed.staged === true) + ':' + String(seed.untracked === true) + ':' + String(seed.path))
|
|
10523
10529
|
const [state, setState] = useState({ loading: false, patch: '', error: null })
|
|
10524
10530
|
const [refreshTick, setRefreshTick] = useState(0)
|
|
10525
10531
|
const [copied, setCopied] = useState(false)
|
|
@@ -10539,7 +10545,12 @@ window.__ModuleLoader__.load({
|
|
|
10539
10545
|
since the route exists): the preview follows the exact file
|
|
10540
10546
|
the user clicked instead of the commit's first file. */
|
|
10541
10547
|
if (seed.path !== undefined && seed.path !== null && seed.path !== '') payload.path = seed.path
|
|
10542
|
-
} else {
|
|
10548
|
+
} else {
|
|
10549
|
+
payload.path = seed.path; payload.staged = seed.staged === true
|
|
10550
|
+
/* untracked files never ride `git diff -- path` (no index entry):
|
|
10551
|
+
the host switches to a --no-index /dev/null new-file patch. */
|
|
10552
|
+
if (seed.untracked === true) payload.untracked = true
|
|
10553
|
+
}
|
|
10543
10554
|
const data = await request('diff', payload)
|
|
10544
10555
|
if (alive === true) setState({ loading: false, patch: data === undefined || data === null || typeof data.patch !== 'string' ? '' : data.patch, error: null })
|
|
10545
10556
|
} catch (error) {
|
package/src/index.js
CHANGED
|
@@ -726,6 +726,38 @@ async function commitDetail(payload) {
|
|
|
726
726
|
async function diff(payload) {
|
|
727
727
|
const cwd = cwdOf(payload)
|
|
728
728
|
const root = await repoRootOf(cwd, payload)
|
|
729
|
+
/* Untracked files have no index entry, so plain `git diff [--cached] -- path`
|
|
730
|
+
is legitimately empty for them — the panel's change list still shows the
|
|
731
|
+
file and the preview must not go blank. `--no-index /dev/null <file>`
|
|
732
|
+
produces the honest "new file" patch; its exit code is 1 when differences
|
|
733
|
+
exist, which is data, not failure (only >1 rejects). */
|
|
734
|
+
if (payload.untracked === true) {
|
|
735
|
+
const rel = requireRelativePath(payload.path)
|
|
736
|
+
const absolute = path.isAbsolute(rel) ? rel : path.join(root, rel)
|
|
737
|
+
/* --no-index reads the file for real, so unlike the plain `git diff --`
|
|
738
|
+
form an absolute path must NOT be allowed to escape the repository. */
|
|
739
|
+
if (absolute !== root && !absolute.startsWith(root + path.sep)) throw badRequest('path must stay inside the repository')
|
|
740
|
+
const result = await runGit(root, ['diff', '--no-color', '--no-ext-diff', '-U3', '--no-index', '--', '/dev/null', absolute])
|
|
741
|
+
if (result.code !== 0 && result.code !== 1) {
|
|
742
|
+
const detail = result.stderr.trim() === '' ? result.stdout.trim() : result.stderr.trim()
|
|
743
|
+
throw new PanelError('git-failed', 'git diff --no-index failed: ' + (detail === '' ? 'exit ' + result.code : detail), 409)
|
|
744
|
+
}
|
|
745
|
+
/* --no-index spells the b/ side with the absolute path we passed; rewrite
|
|
746
|
+
the header lines back to the repository-relative spelling so the preview
|
|
747
|
+
reads like every other patch (body lines are left untouched). */
|
|
748
|
+
let out = result.stdout
|
|
749
|
+
if (out.indexOf(absolute) >= 0 && out.indexOf('\n') !== -1) {
|
|
750
|
+
out = out.split('\n').map((line) => {
|
|
751
|
+
if (line.startsWith('diff --git ') && line.indexOf(absolute) >= 0) return 'diff --git a/' + rel + ' b/' + rel
|
|
752
|
+
/* the b/ side of --no-index carries the absolute path verbatim (its
|
|
753
|
+
leading slash merged into the separator): match '+++ b' + absolute. */
|
|
754
|
+
if (line.startsWith('+++ b' + absolute)) return '+++ b/' + rel
|
|
755
|
+
return line
|
|
756
|
+
}).join('\n')
|
|
757
|
+
}
|
|
758
|
+
const truncated = out.length > MAX_DIFF_CHARS
|
|
759
|
+
return { patch: truncated ? out.slice(0, MAX_DIFF_CHARS) : out, truncated }
|
|
760
|
+
}
|
|
729
761
|
const args = ['diff', '--no-color', '--no-ext-diff', '-U3']
|
|
730
762
|
if (payload.staged === true) args.push('--cached')
|
|
731
763
|
if (typeof payload.hash === 'string' && payload.hash.trim() !== '') {
|
|
@@ -1547,10 +1579,17 @@ async function commitMessage(payload) {
|
|
|
1547
1579
|
commitBusy = true
|
|
1548
1580
|
try {
|
|
1549
1581
|
const cwd = cwdOf(payload)
|
|
1582
|
+
/* The picked repository travels as `repoRoot`, and every other git call
|
|
1583
|
+
threads it through `repoRootOf`. Resolving the root from `cwd` alone made
|
|
1584
|
+
this the one route that answered not-a-repo in a workspace whose own root
|
|
1585
|
+
is not a repository — a workspace holding several sibling repos, which is
|
|
1586
|
+
exactly what the repo picker exists for. */
|
|
1587
|
+
const repoRoot = await repoRootOf(cwd, payload)
|
|
1588
|
+
const scope = { cwd: cwd, repoRoot: repoRoot }
|
|
1550
1589
|
const route = await commitRouteOf(payload)
|
|
1551
|
-
const sums = await summary(
|
|
1552
|
-
const stagedPatch = sums.changes.staged.length > 0 ? (await diff({
|
|
1553
|
-
const input = commitInputOf(stagedPatch, (await diff(
|
|
1590
|
+
const sums = await summary(scope)
|
|
1591
|
+
const stagedPatch = sums.changes.staged.length > 0 ? (await diff(Object.assign({}, scope, { staged: true }))).patch : ''
|
|
1592
|
+
const input = commitInputOf(stagedPatch, (await diff(scope)).patch)
|
|
1554
1593
|
if (input.files.length === 0) {
|
|
1555
1594
|
if (input.dropped > 0) {
|
|
1556
1595
|
throw new PanelError('only-ignored-changes', 'the only changes are lock files or binary files, which are not sent to the model; stage a source change first')
|