@wwkit/opm 1.0.18 → 1.0.20
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 +26 -23
- package/bin/index.js +8 -0
- package/package.json +2 -2
- package/src/binaries/base.js +5 -10
- package/src/cli/groups/env.js +15 -14
- package/src/cli/groups/nvm.js +1 -1
- package/src/cli/groups/overlay.js +7 -7
- package/src/cli/groups/package.js +62 -20
- package/src/cli/groups/phpenv.js +2 -2
- package/src/cli/groups/ping.js +83 -85
- package/src/cli/groups/proc.js +14 -14
- package/src/cli/groups/version-manager.js +6 -10
- package/src/cli/helpers/args.js +4 -46
- package/src/cli/index.js +24 -2
- package/src/harnesses/base.js +2 -1
- package/src/harnesses/opencode/clear.js +29 -26
- package/src/harnesses/opencode/index.js +1 -2
- package/src/managers/apt.js +16 -0
- package/src/managers/brew.js +20 -1
- package/src/managers/bun.js +23 -0
- package/src/managers/composer.js +11 -0
- package/src/managers/dnf.js +16 -0
- package/src/managers/node.js +1 -1
- package/src/managers/npm.js +12 -0
- package/src/managers/php.js +2 -2
- package/src/managers/pip.js +16 -0
- package/src/managers/runtime.js +1 -1
- package/src/managers/winget.js +12 -0
- package/src/tools/clipboard.js +68 -0
- package/src/tools/ftp/index.js +11 -11
- package/src/tools/git/index.js +30 -13
- package/src/tools/share/index.js +327 -139
- package/src/tools/share/server.js +21 -5
- package/src/tools/webwork/index.js +6 -2
package/src/managers/brew.js
CHANGED
|
@@ -335,10 +335,29 @@ export class BrewManager extends PackageManager {
|
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
async cleanCache() {
|
|
338
|
-
this._execInherit(['cleanup', '-s'
|
|
338
|
+
this._execInherit(['cleanup', '-s'])
|
|
339
339
|
return { action: 'cache cleaned' }
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
/**
|
|
343
|
+
* 列出已安装的 Homebrew 包(系统级,无项目作用域)
|
|
344
|
+
* @returns {Promise<{ name: string, version: string }[]>}
|
|
345
|
+
*/
|
|
346
|
+
async listInstalled(opts = {}) {
|
|
347
|
+
if (opts.global === false) return []
|
|
348
|
+
const out = this._exec(['list', '--versions'], { allowNonZero: true })
|
|
349
|
+
const items = []
|
|
350
|
+
// 行格式: name v1 v2 ...(多版本取最新)
|
|
351
|
+
for (const line of out.split('\n')) {
|
|
352
|
+
const m = line.trim().match(/^(\S+)\s+(.+)$/)
|
|
353
|
+
if (m) {
|
|
354
|
+
const versions = m[2].trim().split(/\s+/)
|
|
355
|
+
items.push({ name: m[1], version: versions[versions.length - 1] })
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return items
|
|
359
|
+
}
|
|
360
|
+
|
|
342
361
|
/**
|
|
343
362
|
* 查询 brew 安装路径和包目录
|
|
344
363
|
* @returns {{ name: string, install: string, prefix: string, cellar: string }}
|
package/src/managers/bun.js
CHANGED
|
@@ -272,6 +272,29 @@ export class BunManager extends PackageManager {
|
|
|
272
272
|
return { action: 'cache cleaned' }
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
/**
|
|
276
|
+
* 列出已安装包(bun pm ls 文本树解析;-g 列全局)
|
|
277
|
+
* @param {{ global?: boolean }} [opts]
|
|
278
|
+
* @returns {Promise<{ name: string, version: string }[]>}
|
|
279
|
+
*/
|
|
280
|
+
async listInstalled(opts = {}) {
|
|
281
|
+
const args = opts.global ? ['pm', 'ls', '-g', '--all'] : ['pm', 'ls', '--all']
|
|
282
|
+
const out = this._exec(args, { allowNonZero: true })
|
|
283
|
+
const items = []
|
|
284
|
+
for (const line of out.split('\n')) {
|
|
285
|
+
const cleaned = line.replace(/^[\s├└─│]+/, '').trim()
|
|
286
|
+
if (!cleaned) continue
|
|
287
|
+
// scoped 包:@babel/core@7.0.0 → 最后一个 @ 为版本分隔
|
|
288
|
+
const idx = cleaned.lastIndexOf('@')
|
|
289
|
+
if (idx > 0) {
|
|
290
|
+
const name = cleaned.slice(0, idx)
|
|
291
|
+
const version = cleaned.slice(idx + 1)
|
|
292
|
+
if (name && version) items.push({ name, version: version.replace(/^v/, '') })
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return items
|
|
296
|
+
}
|
|
297
|
+
|
|
275
298
|
/**
|
|
276
299
|
* 查询 bun 安装路径和包目录
|
|
277
300
|
* @returns {{ name: string, install: string, globalDir: string, cacheDir: string }}
|
package/src/managers/composer.js
CHANGED
|
@@ -344,6 +344,17 @@ export class ComposerManager extends PackageManager {
|
|
|
344
344
|
return { action: 'cache cleaned' }
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
/**
|
|
348
|
+
* 列出已安装包(缺省读当前项目 vendor/composer/installed.json,global 读全局 installed.json)
|
|
349
|
+
* @param {{ global?: boolean }} [opts]
|
|
350
|
+
* @returns {Promise<{ name: string, version: string }[]>}
|
|
351
|
+
*/
|
|
352
|
+
async listInstalled(opts = {}) {
|
|
353
|
+
const packages = opts.global ? this._readGlobalInstalledJson() : this._readInstalledJson()
|
|
354
|
+
if (!packages) return []
|
|
355
|
+
return packages.map((p) => ({ name: p.name, version: p.version || 'unknown' }))
|
|
356
|
+
}
|
|
357
|
+
|
|
347
358
|
/**
|
|
348
359
|
* 查询 composer 安装路径和包目录
|
|
349
360
|
* @returns {{ name: string, install: string, globalHome: string, vendorDir: string }}
|
package/src/managers/dnf.js
CHANGED
|
@@ -342,6 +342,22 @@ export class DnfManager extends PackageManager {
|
|
|
342
342
|
return { action: 'cache cleaned' }
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
+
/**
|
|
346
|
+
* 列出系统已安装包(系统级管理器,无项目作用域)
|
|
347
|
+
* @returns {Promise<{ name: string, version: string }[]>}
|
|
348
|
+
*/
|
|
349
|
+
async listInstalled(opts = {}) {
|
|
350
|
+
if (opts.global === false) return []
|
|
351
|
+
const out = this._exec(['list', 'installed', '--quiet'], { allowNonZero: true })
|
|
352
|
+
const items = []
|
|
353
|
+
// 行格式: name.arch version repo
|
|
354
|
+
for (const line of out.split('\n')) {
|
|
355
|
+
const m = line.trim().match(/^(\S+?)\.\S+\s+(\S+)\s+\S+/)
|
|
356
|
+
if (m) items.push({ name: m[1], version: m[2] })
|
|
357
|
+
}
|
|
358
|
+
return items
|
|
359
|
+
}
|
|
360
|
+
|
|
345
361
|
/**
|
|
346
362
|
* 查询 dnf 安装路径和包目录
|
|
347
363
|
* @returns {{ name: string, install: string, cacheDir: string, repoDir: string }}
|
package/src/managers/node.js
CHANGED
|
@@ -55,7 +55,7 @@ export function installNvm(proxy) {
|
|
|
55
55
|
env.http_proxy = proxy
|
|
56
56
|
env.https_proxy = proxy
|
|
57
57
|
}
|
|
58
|
-
execSync(`git clone "${mirror}" "${nvmDir}"`, { stdio:
|
|
58
|
+
execSync(`git clone "${mirror}" "${nvmDir}"`, { stdio: shell.stdioForExec(true), shell: shell.resolveBash(), env })
|
|
59
59
|
const lines = [
|
|
60
60
|
`export NVM_DIR="${nvmDir}"`,
|
|
61
61
|
`[ -s "\\$NVM_DIR/nvm.sh" ] && \\. "\\$NVM_DIR/nvm.sh"`,
|
package/src/managers/npm.js
CHANGED
|
@@ -505,6 +505,18 @@ export class NpmManager extends PackageManager {
|
|
|
505
505
|
return { action: 'cache cleaned' }
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
/**
|
|
509
|
+
* 列出已安装包
|
|
510
|
+
* @param {{ global?: boolean }} [opts] - global=true 列全局,缺省列当前项目
|
|
511
|
+
* @returns {Promise<{ name: string, version: string }[]>}
|
|
512
|
+
*/
|
|
513
|
+
async listInstalled(opts = {}) {
|
|
514
|
+
const args = opts.global ? ['ls', '-g', '--depth=0'] : ['ls', '--depth=0']
|
|
515
|
+
const data = this._execJson(args)
|
|
516
|
+
const deps = (data && data.dependencies) || {}
|
|
517
|
+
return Object.entries(deps).map(([name, d]) => ({ name, version: (d.version || '').replace(/^v/, '') }))
|
|
518
|
+
}
|
|
519
|
+
|
|
508
520
|
/**
|
|
509
521
|
* 查询 npm 安装路径和包目录
|
|
510
522
|
* @returns {{ name: string, install: string, globalDir: string, localDir: string, cacheDir: string, prefix: string }}
|
package/src/managers/php.js
CHANGED
|
@@ -51,9 +51,9 @@ export function isPhpenvInstalled() {
|
|
|
51
51
|
export function installPhpenv() {
|
|
52
52
|
const mirror = getActiveRegistry('phpenv')
|
|
53
53
|
const root = getPhpenvRoot()
|
|
54
|
-
execSync(`git clone "${mirror}" "${root}"`, { stdio:
|
|
54
|
+
execSync(`git clone "${mirror}" "${root}"`, { stdio: shell.stdioForExec(true), shell: shell.resolveBash() })
|
|
55
55
|
execSync(`git clone "${PHP_BUILD_GIT}" "${path.join(root, 'plugins', 'php-build')}"`, {
|
|
56
|
-
stdio:
|
|
56
|
+
stdio: shell.stdioForExec(true),
|
|
57
57
|
shell: shell.resolveBash(),
|
|
58
58
|
})
|
|
59
59
|
const lines = [
|
package/src/managers/pip.js
CHANGED
|
@@ -557,6 +557,22 @@ export class PipManager extends PackageManager {
|
|
|
557
557
|
return { action: 'cache cleaned' }
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
+
/**
|
|
561
|
+
* 列出已安装包
|
|
562
|
+
* @param {{ global?: boolean }} [opts] - global=true 用系统级 Python 环境,缺省当前环境
|
|
563
|
+
* @returns {Promise<{ name: string, version: string }[]>}
|
|
564
|
+
*/
|
|
565
|
+
async listInstalled(opts = {}) {
|
|
566
|
+
const out = this._execPip(['list', '--format=json', '--disable-pip-version-check'], { allowNonZero: true, global: opts.global })
|
|
567
|
+
try {
|
|
568
|
+
const data = JSON.parse(out)
|
|
569
|
+
if (!Array.isArray(data)) return []
|
|
570
|
+
return data.map((i) => ({ name: i.name, version: i.version || '' }))
|
|
571
|
+
} catch {
|
|
572
|
+
return []
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
560
576
|
/**
|
|
561
577
|
* 查询 pip 安装路径和包目录
|
|
562
578
|
* @returns {{ name: string, install: string, sitePackages: string, userSite: string }}
|
package/src/managers/runtime.js
CHANGED
|
@@ -127,7 +127,7 @@ export class RuntimeManager extends PackageManager {
|
|
|
127
127
|
throw new Error(this.cfg.toolMissingMsg)
|
|
128
128
|
}
|
|
129
129
|
const result = spawnSync(this._cmd(args), {
|
|
130
|
-
stdio:
|
|
130
|
+
stdio: shell.stdioForExec(this.cfg.needsBash),
|
|
131
131
|
shell: shell.shellForExec(this.cfg.needsBash),
|
|
132
132
|
env: this._buildEnv(opts.proxy),
|
|
133
133
|
})
|
package/src/managers/winget.js
CHANGED
|
@@ -242,6 +242,18 @@ export class WingetManager extends PackageManager {
|
|
|
242
242
|
return { action: 'winget has no cache clean command' }
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
/**
|
|
246
|
+
* 列出系统已安装包(winget list 表格解析)
|
|
247
|
+
* @returns {Promise<{ name: string, version: string }[]>}
|
|
248
|
+
*/
|
|
249
|
+
async listInstalled(opts = {}) {
|
|
250
|
+
const out = this._exec(['list'], { allowNonZero: true })
|
|
251
|
+
const rows = this._parseTable(out)
|
|
252
|
+
return rows
|
|
253
|
+
.filter((r) => r.Name)
|
|
254
|
+
.map((r) => ({ name: r.Name, version: r.Version || '' }))
|
|
255
|
+
}
|
|
256
|
+
|
|
245
257
|
/**
|
|
246
258
|
* 查询 winget 安装路径
|
|
247
259
|
* @returns {{ name: string, install: string }}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 系统剪切板读写工具
|
|
3
|
+
*
|
|
4
|
+
* 跨平台读取/写入系统级剪切板:
|
|
5
|
+
* - darwin: pbpaste / pbcopy
|
|
6
|
+
* - linux: 优先 wl-paste / wl-copy(Wayland),回退 xclip / xsel
|
|
7
|
+
* - win32: PowerShell Get-Clipboard / Set-Clipboard
|
|
8
|
+
* 读取失败(命令不存在/非零退出)返回空字符串,调用方用 trim 判断空。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawnSync } from 'node:child_process'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 执行外部命令并捕获输出
|
|
15
|
+
* @param {string} cmd
|
|
16
|
+
* @param {string[]} args
|
|
17
|
+
* @param {{input?: string}} [opts]
|
|
18
|
+
* @returns {{ok: boolean, stdout: string, stderr: string}}
|
|
19
|
+
*/
|
|
20
|
+
function run(cmd, args, opts = {}) {
|
|
21
|
+
const res = spawnSync(cmd, args, { encoding: 'utf8', ...opts })
|
|
22
|
+
if (res.error) {
|
|
23
|
+
return { ok: false, stdout: '', stderr: res.error.message }
|
|
24
|
+
}
|
|
25
|
+
return { ok: res.status === 0, stdout: res.stdout || '', stderr: res.stderr || '' }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 读取系统剪切板内容(失败返回空字符串)
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
export function readClipboard() {
|
|
33
|
+
const p = process.platform
|
|
34
|
+
if (p === 'darwin') {
|
|
35
|
+
return run('pbpaste', []).stdout
|
|
36
|
+
}
|
|
37
|
+
if (p === 'win32') {
|
|
38
|
+
const r = run('powershell', [
|
|
39
|
+
'-NoProfile', '-Command',
|
|
40
|
+
'[Console]::Out.Write((Get-Clipboard -Raw -ErrorAction SilentlyContinue))',
|
|
41
|
+
])
|
|
42
|
+
return r.ok ? r.stdout : ''
|
|
43
|
+
}
|
|
44
|
+
// linux
|
|
45
|
+
const r = run('bash', [
|
|
46
|
+
'-c',
|
|
47
|
+
'wl-paste --no-newline 2>/dev/null || xclip -selection clipboard -o 2>/dev/null || xsel --clipboard --output 2>/dev/null',
|
|
48
|
+
])
|
|
49
|
+
return r.ok ? r.stdout : ''
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 写入系统剪切板
|
|
54
|
+
* @param {string} text
|
|
55
|
+
*/
|
|
56
|
+
export function writeClipboard(text) {
|
|
57
|
+
const input = String(text ?? '')
|
|
58
|
+
const p = process.platform
|
|
59
|
+
if (p === 'darwin') {
|
|
60
|
+
run('pbcopy', [], { input })
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if (p === 'win32') {
|
|
64
|
+
run('powershell', ['-NoProfile', '-Command', 'Set-Clipboard -Value $input'], { input })
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
run('bash', ['-c', 'wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || xsel --clipboard --input 2>/dev/null'], { input })
|
|
68
|
+
}
|
package/src/tools/ftp/index.js
CHANGED
|
@@ -196,7 +196,7 @@ export class FtpGroup {
|
|
|
196
196
|
console.error('Usage: opm ftp upload <file> [-n name] [-s subdir]')
|
|
197
197
|
process.exit(1)
|
|
198
198
|
}
|
|
199
|
-
const name = parsed.flags.name ||
|
|
199
|
+
const name = parsed.flags.name || ''
|
|
200
200
|
const subdir = parsed.flags.subdir || parsed.flags.s || ''
|
|
201
201
|
const cfg = getFtpConfig()
|
|
202
202
|
|
|
@@ -242,14 +242,14 @@ export class FtpGroup {
|
|
|
242
242
|
async _rsync(parsed) {
|
|
243
243
|
const local = parsed.positional[0]
|
|
244
244
|
if (!local) {
|
|
245
|
-
console.error('Usage: opm ftp rsync <local> [-s subdir] [-x pattern] [--dry-run] [--
|
|
245
|
+
console.error('Usage: opm ftp rsync <local> [-s subdir] [-x pattern] [--dry-run] [--delete]')
|
|
246
246
|
process.exit(1)
|
|
247
247
|
}
|
|
248
248
|
const subdir = parsed.flags.subdir || parsed.flags.s || ''
|
|
249
249
|
const excludes = parsed.flags.exclude || parsed.flags.x || []
|
|
250
250
|
const excludeList = Array.isArray(excludes) ? excludes : [excludes]
|
|
251
251
|
const dryRun = parsed.flags['dry-run'] === 'true'
|
|
252
|
-
const
|
|
252
|
+
const deleteRemote = parsed.flags.delete === 'true'
|
|
253
253
|
|
|
254
254
|
const cfg = getFtpConfig()
|
|
255
255
|
const localDir = path.resolve(local)
|
|
@@ -282,7 +282,7 @@ export class FtpGroup {
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
const deletePlan = []
|
|
285
|
-
if (
|
|
285
|
+
if (deleteRemote) {
|
|
286
286
|
for (const [rel] of remoteFiles) {
|
|
287
287
|
if (!toUpload.includes(rel)) {
|
|
288
288
|
deletePlan.push(rel)
|
|
@@ -335,9 +335,9 @@ export class FtpGroup {
|
|
|
335
335
|
skippedSame: skippedSame.length,
|
|
336
336
|
skippedExcluded,
|
|
337
337
|
failed,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
338
|
+
deleted: deleted.length,
|
|
339
|
+
deletedFiles: deleted,
|
|
340
|
+
deleteFailed,
|
|
341
341
|
})
|
|
342
342
|
}
|
|
343
343
|
|
|
@@ -370,7 +370,7 @@ Actions:
|
|
|
370
370
|
uninstall Uninstall basic-ftp
|
|
371
371
|
upgrade Upgrade basic-ftp to latest (uses opm npm registry + proxy)
|
|
372
372
|
connect Test FTP connection
|
|
373
|
-
upload <file> [
|
|
373
|
+
upload <file> [--name name] [-s subdir] Upload file (local path or HTTP URL)
|
|
374
374
|
rsync <local> [options] Recursive upload directory (FTP-side dedup)
|
|
375
375
|
download <remote> <local> Download file from FTP
|
|
376
376
|
root Show configured remote root directory
|
|
@@ -380,7 +380,7 @@ rsync options:
|
|
|
380
380
|
-s, --subdir <dir> Remote subdirectory (under remote_root)
|
|
381
381
|
-x, --exclude <pattern> Exclude glob pattern (repeatable)
|
|
382
382
|
--dry-run Preview without uploading
|
|
383
|
-
--
|
|
383
|
+
--delete Delete remote files not in local
|
|
384
384
|
|
|
385
385
|
Options:
|
|
386
386
|
-p, --proxy <url> Proxy for install/upgrade (overrides config proxy.active)
|
|
@@ -393,12 +393,12 @@ Examples:
|
|
|
393
393
|
opm ftp upgrade
|
|
394
394
|
opm ftp connect
|
|
395
395
|
opm ftp upload ./img.png
|
|
396
|
-
opm ftp upload ./img.png
|
|
396
|
+
opm ftp upload ./img.png --name cover -s banner
|
|
397
397
|
opm ftp upload https://example.com/a.jpg -n cover.jpg
|
|
398
398
|
opm ftp rsync ./webwork/
|
|
399
399
|
opm ftp rsync ./webwork/ -s deploy -x "vendor/*" -x "*.log"
|
|
400
400
|
opm ftp rsync ./webwork/ --dry-run
|
|
401
|
-
opm ftp rsync ./webwork/ --
|
|
401
|
+
opm ftp rsync ./webwork/ --delete
|
|
402
402
|
opm ftp download 20260913/cover.jpg ./cover.jpg
|
|
403
403
|
opm ftp root
|
|
404
404
|
`)
|
package/src/tools/git/index.js
CHANGED
|
@@ -40,6 +40,20 @@ export class GitGroup {
|
|
|
40
40
|
this.desc = 'Git config management (init/config/user/log/export)'
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* 当前 action 下的上下文布尔标志
|
|
45
|
+
* 同名标志在不同 action 中语义不同(-f:init=force / export=from;--unset:config 带值 / export mark 布尔),
|
|
46
|
+
* 由 action 决定解析方式,避免布尔标志吞掉后续参数。
|
|
47
|
+
* @param {string} action
|
|
48
|
+
* @returns {string[]}
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
_booleanFlags(action) {
|
|
52
|
+
if (action === 'init') return ['f', 'force']
|
|
53
|
+
if (action === 'export') return ['remove', 'unset']
|
|
54
|
+
return []
|
|
55
|
+
}
|
|
56
|
+
|
|
43
57
|
async run(argv) {
|
|
44
58
|
const [action, ...rest] = argv
|
|
45
59
|
|
|
@@ -48,7 +62,7 @@ export class GitGroup {
|
|
|
48
62
|
return
|
|
49
63
|
}
|
|
50
64
|
|
|
51
|
-
const parsed = parseFlags(rest)
|
|
65
|
+
const parsed = parseFlags(rest, { booleans: this._booleanFlags(action) })
|
|
52
66
|
|
|
53
67
|
switch (action) {
|
|
54
68
|
case 'version':
|
|
@@ -146,7 +160,7 @@ export class GitGroup {
|
|
|
146
160
|
|
|
147
161
|
async _user(parsed) {
|
|
148
162
|
const scope = this._getScope(parsed)
|
|
149
|
-
const name = parsed.flags.
|
|
163
|
+
const name = parsed.flags.name || ''
|
|
150
164
|
const email = parsed.flags.e || parsed.flags.email || ''
|
|
151
165
|
|
|
152
166
|
if (name || email) {
|
|
@@ -209,7 +223,7 @@ export class GitGroup {
|
|
|
209
223
|
const workDir = parsed.flags.d || parsed.flags.dir || process.cwd()
|
|
210
224
|
const ref = parsed.positional[1]
|
|
211
225
|
|
|
212
|
-
if (parsed.flags.unset === 'true') {
|
|
226
|
+
if (parsed.flags.remove === 'true' || parsed.flags.unset === 'true') {
|
|
213
227
|
const ok = unsetMark(workDir)
|
|
214
228
|
output({ mark: 'removed', ok })
|
|
215
229
|
return
|
|
@@ -230,7 +244,7 @@ export class GitGroup {
|
|
|
230
244
|
console.error('\nTips:')
|
|
231
245
|
console.error(' opm git log -d <dir> -n 10 # view commit history without cd')
|
|
232
246
|
console.error(' opm git export mark <commit> # change diff base to another commit')
|
|
233
|
-
|
|
247
|
+
console.error(' opm git export mark --remove # remove current mark')
|
|
234
248
|
console.error(' opm git export # export diff (mark -> HEAD)')
|
|
235
249
|
this._printRecentLog(workDir)
|
|
236
250
|
return
|
|
@@ -250,7 +264,7 @@ export class GitGroup {
|
|
|
250
264
|
process.exit(1)
|
|
251
265
|
}
|
|
252
266
|
|
|
253
|
-
const fromRaw = parsed.flags.
|
|
267
|
+
const fromRaw = parsed.flags.from || ''
|
|
254
268
|
let fromRef
|
|
255
269
|
if (fromRaw) {
|
|
256
270
|
const n = parseInt(fromRaw, 10)
|
|
@@ -271,7 +285,7 @@ export class GitGroup {
|
|
|
271
285
|
if (!outFile) {
|
|
272
286
|
const opts = { encoding: 'utf8', timeout: 5000, cwd: resolvedDir, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
273
287
|
const gitRoot = execSync('git rev-parse --show-toplevel', opts).trim()
|
|
274
|
-
const name = parsed.flags.
|
|
288
|
+
const name = parsed.flags.name || ''
|
|
275
289
|
const fileName = name ? `${name}.diff` : (() => {
|
|
276
290
|
const fromShort = execSync(`git rev-parse --short ${fromRef}`, opts).trim()
|
|
277
291
|
const toShort = execSync('git rev-parse --short HEAD', opts).trim()
|
|
@@ -331,7 +345,7 @@ Export subcommands:
|
|
|
331
345
|
export mark [-d dir] Show current diff base mark
|
|
332
346
|
export mark 0|HEAD [-d dir] Set diff base mark to current HEAD
|
|
333
347
|
export mark <commit> [-d dir] Set diff base mark to a commit
|
|
334
|
-
export mark --
|
|
348
|
+
export mark --remove [-d dir] Remove the diff base mark
|
|
335
349
|
export [-d dir] Export diff (mark -> HEAD)
|
|
336
350
|
export -f <n> [-d dir] Export diff (HEAD~n -> HEAD, override mark)
|
|
337
351
|
export -f <ref> [-d dir] Export diff (<ref> -> HEAD, override mark)
|
|
@@ -344,11 +358,15 @@ Options:
|
|
|
344
358
|
-n, --count <n> Number of commits to show (log, default: 10)
|
|
345
359
|
-e, --email <email> User email (user)
|
|
346
360
|
-o, --output <file> Output file path (export)
|
|
347
|
-
|
|
361
|
+
--from <n>|<ref> Override from ref (export)
|
|
348
362
|
-f, --force Force overwrite existing config (init)
|
|
349
|
-
--unset <key> Unset a config key (config)
|
|
363
|
+
--unset <key> Unset a config key (config only)
|
|
364
|
+
--remove Remove the diff base mark (export mark)
|
|
350
365
|
-h, --help Show this help
|
|
351
366
|
|
|
367
|
+
Note: -n 在 log 中为 --count 数量,user/export 中用 --name 名称(无短形式);
|
|
368
|
+
-f 在 init 中为 --force 布尔,export 中用 --from 带值(无短形式)。
|
|
369
|
+
|
|
352
370
|
Examples:
|
|
353
371
|
opm git version Show installed git version
|
|
354
372
|
opm git install Install git via system package manager
|
|
@@ -365,12 +383,11 @@ Examples:
|
|
|
365
383
|
opm git export mark 0 Set diff base to current HEAD (cwd)
|
|
366
384
|
opm git export mark HEAD Set diff base to current HEAD (cwd)
|
|
367
385
|
opm git export mark 3af967b Set diff base at commit (cwd)
|
|
368
|
-
opm git export mark --
|
|
369
|
-
opm git export mark abc -d /repo Set diff base at commit in /repo
|
|
386
|
+
opm git export mark --remove Remove diff base mark (cwd) opm git export mark abc -d /repo Set diff base at commit in /repo
|
|
370
387
|
opm git export Export diff in current repo (mark -> HEAD)
|
|
371
|
-
opm git export
|
|
388
|
+
opm git export --from 2 Override: HEAD~2 -> HEAD (current repo)
|
|
372
389
|
opm git export -d /path/to/repo Export diff in specified repo
|
|
373
|
-
opm git export
|
|
390
|
+
opm git export --name feature Save as feature.diff
|
|
374
391
|
opm git import file.diff Import diff file (current repo)
|
|
375
392
|
opm git import file.diff -d /repo Import diff file into specified repo
|
|
376
393
|
`)
|