From e79b82d9e138166975c0c7fe4a5c651b9ddcabfa Mon Sep 17 00:00:00 2001 From: kahole Date: Wed, 5 Aug 2020 21:13:24 +0200 Subject: [PATCH] adds submodule commands --- README.md | 2 +- package.json | 13 +++ src/commands/fetchingCommands.ts | 10 +++ src/commands/statusCommands.ts | 3 +- src/commands/submodulesCommands.ts | 133 +++++++++++++++++++++++++++++ src/extension.ts | 2 + src/models/magitState.ts | 3 +- src/views/submoduleListView.ts | 31 +++++++ 8 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 src/commands/submodulesCommands.ts create mode 100644 src/views/submoduleListView.ts diff --git a/README.md b/README.md index 35a48bf..3f116fd 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,8 @@ _Feature requests as well as issues are welcome_ - Diffing (a lot missing) - More logging features (https://github.com/kahole/edamagit/pull/40) - Bisecting - - Submodules - Patches + - Subtrees ### Interface - Config menus diff --git a/package.json b/package.json index 86c2663..1899a3c 100644 --- a/package.json +++ b/package.json @@ -167,6 +167,10 @@ "command": "magit.stashing", "title": "Magit Stashing" }, + { + "command": "magit.submodules", + "title": "Magit Submodules" + }, { "command": "magit.fetching", "title": "Magit Fetching" @@ -312,6 +316,10 @@ "command": "magit.stashing", "when": "editorLangId == magit" }, + { + "command": "magit.submodules", + "when": "editorLangId == magit" + }, { "command": "magit.fetching", "when": "editorLangId == magit" @@ -536,6 +544,11 @@ "key": "z", "when": "editorTextFocus && editorLangId == 'magit' && vim.mode =~ /^(?!SearchInProgressMode|CommandlineInProgress).*$/" }, + { + "command": "magit.submodules", + "key": "o", + "when": "editorTextFocus && editorLangId == 'magit' && vim.mode =~ /^(?!SearchInProgressMode|CommandlineInProgress).*$/" + }, { "command": "magit.fetching", "key": "f", diff --git a/src/commands/fetchingCommands.ts b/src/commands/fetchingCommands.ts index 68ea59f..21346c3 100644 --- a/src/commands/fetchingCommands.ts +++ b/src/commands/fetchingCommands.ts @@ -25,6 +25,10 @@ export async function fetching(repository: MagitRepository): Promise { fetchingMenuItems.push({ label: 'o', description: 'another branch', action: fetchAnotherBranch }); + if (repository.state.submodules.length) { + fetchingMenuItems.push({ label: 's', description: 'submodules', action: fetchSubmodules }); + } + const switches: Switch[] = [ { shortName: '-p', longName: '--prune', description: 'Prune deleted branches' } ]; @@ -74,4 +78,10 @@ async function fetchAnotherBranch({ repository, switches }: MenuState) { return gitRun(repository, args); } } +} + +export async function fetchSubmodules({ repository, switches }: MenuState) { + + const args = ['fetch', '--verbose', '--recurse-submodules', ...MenuUtil.switchesToArgs(switches)]; + return gitRun(repository, args); } \ No newline at end of file diff --git a/src/commands/statusCommands.ts b/src/commands/statusCommands.ts index 0791641..34007fe 100644 --- a/src/commands/statusCommands.ts +++ b/src/commands/statusCommands.ts @@ -77,7 +77,7 @@ export async function internalMagitStatus(repository: MagitRepository): Promise< const untrackedFiles: MagitChange[] = repository.state.workingTreeChanges.length > workingTreeChanges_NoUntracked.length ? - (await gitRun(repository, ['ls-files', '--others', '--exclude-standard', '--directory', '--no-empty-directory'])) + (await gitRun(repository, ['ls-files', '--others', '--exclude-standard', '--directory', '--no-empty-directory'], {}, LogLevel.None)) .stdout .replace(Constants.FinalLineBreakRegex, '') .split(Constants.LineSplitterRegex) @@ -168,6 +168,7 @@ export async function internalMagitStatus(repository: MagitRepository): Promise< branches: repository.state.refs.filter(ref => ref.type === RefType.Head), remotes, tags: repository.state.refs.filter(ref => ref.type === RefType.Tag), + submodules: repository.state.submodules, latestGitError: repository.magitState?.latestGitError }; } diff --git a/src/commands/submodulesCommands.ts b/src/commands/submodulesCommands.ts new file mode 100644 index 0000000..87016e8 --- /dev/null +++ b/src/commands/submodulesCommands.ts @@ -0,0 +1,133 @@ +import { MagitRepository } from '../models/magitRepository'; +import { MenuUtil, MenuState } from '../menu/menu'; +import { gitRun } from '../utils/gitRawRunner'; +import { window, workspace } from 'vscode'; +import { fetchSubmodules } from './fetchingCommands'; +import SubmoduleListView from '../views/submoduleListView'; +import { views } from '../extension'; + +const submodulesMenu = { + title: 'Submodules', + commands: [ + { label: 'a', description: 'Add', action: add }, + { label: 'r', description: 'Register', action: init }, + { label: 'p', description: 'Populate', action: populate }, + { label: 'u', description: 'Update', action: update }, + { label: 's', description: 'Synchronize', action: sync }, + { label: 'd', description: 'Unpopulate', action: unpopulate }, + { label: 'k', description: 'Remove', action: remove }, + { label: 'l', description: 'List all modules', action: listAll }, + { label: 'f', description: 'Fetch all modules', action: fetchAll }, + ] +}; + +export async function submodules(repository: MagitRepository) { + + const switches = [ + { shortName: '-f', longName: '--force', description: 'Force' }, + { shortName: '-r', longName: '--recursive', description: 'Recursive' }, + { shortName: '-N', longName: '--no-fetch', description: 'Do not fetch' }, + { shortName: '-C', longName: '--checkout', description: 'Checkout tip' }, + { shortName: '-R', longName: '--rebase', description: 'Rebase onto tip' }, + { shortName: '-M', longName: '--merge', description: 'Merge tip' }, + { shortName: '-U', longName: '--remote', description: 'Use upstream tip' } + ]; + + return MenuUtil.showMenu(submodulesMenu, { repository, switches }); +} + +async function add({ repository, switches }: MenuState) { + + const submoduleRemote = await window.showInputBox({ prompt: `Add submodule (remote url)` }); + + if (submoduleRemote) { + + const args = ['submodule', 'add', ...MenuUtil.switchesToArgs(switches), submoduleRemote]; + return await gitRun(repository, args); + } +} + +async function init({ repository, switches }: MenuState) { + + const submodule = await pickSubmodule(repository, 'Populate module'); + + if (submodule) { + const args = ['submodule', 'init', ...MenuUtil.switchesToArgs(switches), '--', submodule]; + return await gitRun(repository, args); + } +} + +async function populate({ repository, switches }: MenuState) { + + const submodule = await pickSubmodule(repository, 'Populate module'); + + if (submodule) { + const args = ['submodule', 'update', '--init', ...MenuUtil.switchesToArgs(switches), '--', submodule]; + return await gitRun(repository, args); + } +} + +async function update({ repository, switches }: MenuState) { + + const submodule = await pickSubmodule(repository, 'Update module'); + + if (submodule) { + const args = ['submodule', 'update', ...MenuUtil.switchesToArgs(switches), '--', submodule]; + return await gitRun(repository, args); + } +} + +async function sync({ repository, switches }: MenuState) { + + const submodule = await pickSubmodule(repository, 'Synchronize module'); + + if (submodule) { + const args = ['submodule', 'sync', ...MenuUtil.switchesToArgs(switches), '--', submodule]; + return await gitRun(repository, args); + } +} + +async function unpopulate({ repository, switches }: MenuState) { + + const submodule = await pickSubmodule(repository, 'Unpopulate module'); + + if (submodule) { + const args = ['submodule', 'deinit', ...MenuUtil.switchesToArgs(switches), '--', submodule]; + return await gitRun(repository, args); + } +} + +async function remove({ repository, switches }: MenuState) { + + const submodule = await pickSubmodule(repository, 'Remove module'); + + if (submodule) { + + const absorbArgs = ['submodule', 'absorbgitdirs', '--', submodule]; + const deinitArgs = ['submodule', 'deinit', '--', submodule]; + const removeArgs = ['rm', '--', submodule]; + + await gitRun(repository, absorbArgs); + await gitRun(repository, deinitArgs); + return await gitRun(repository, removeArgs); + } +} + +async function listAll({ repository, switches }: MenuState) { + const uri = SubmoduleListView.encodeLocation(repository); + + if (!views.has(uri.toString())) { + views.set(uri.toString(), new SubmoduleListView(uri, repository.magitState!)); + } + + return workspace.openTextDocument(uri) + .then(doc => window.showTextDocument(doc, { preview: false })); +} + +function fetchAll({ repository, switches }: MenuState) { + return fetchSubmodules({ repository, switches }); +} + +async function pickSubmodule(repository: MagitRepository, prompt: string): Promise { + return await window.showQuickPick(repository.magitState!.submodules.map(r => r.name), { placeHolder: prompt }); +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 4de50ce..e94456c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -39,6 +39,7 @@ import { reverting } from './commands/revertingCommands'; import { reverseAtPoint } from './commands/reverseAtPointCommands'; import { blameFile } from './commands/blamingCommands'; import { copySectionValueCommand } from './commands/copySectionValueCommands'; +import { submodules } from './commands/submodulesCommands'; export const magitRepositories: Map = new Map(); export const views: Map = new Map(); @@ -104,6 +105,7 @@ export function activate(context: ExtensionContext) { context.subscriptions.push(commands.registerTextEditorCommand('magit.ignoring', Command.primeRepo(ignoring))); context.subscriptions.push(commands.registerTextEditorCommand('magit.running', Command.primeRepo(running))); context.subscriptions.push(commands.registerTextEditorCommand('magit.worktree', Command.primeRepo(worktree))); + context.subscriptions.push(commands.registerTextEditorCommand('magit.submodules', Command.primeRepo(submodules))); context.subscriptions.push(commands.registerTextEditorCommand('magit.process-log', Command.primeRepo(processView, false))); context.subscriptions.push(commands.registerTextEditorCommand('magit.visit-at-point', Command.primeRepoAndView(magitVisitAtPoint, false))); diff --git a/src/models/magitState.ts b/src/models/magitState.ts index 0495ce2..b374c16 100644 --- a/src/models/magitState.ts +++ b/src/models/magitState.ts @@ -1,4 +1,4 @@ -import { Commit, Ref } from '../typings/git'; +import { Commit, Ref, Submodule } from '../typings/git'; import { MagitChange } from './magitChange'; import { Stash } from '../common/gitApiExtensions'; import { MagitBranch } from './magitBranch'; @@ -23,5 +23,6 @@ export interface MagitState { readonly branches: Ref[]; readonly remotes: MagitRemote[]; readonly tags: Ref[]; + readonly submodules: Submodule[]; latestGitError?: string; } \ No newline at end of file diff --git a/src/views/submoduleListView.ts b/src/views/submoduleListView.ts new file mode 100644 index 0000000..afec00a --- /dev/null +++ b/src/views/submoduleListView.ts @@ -0,0 +1,31 @@ +import * as Constants from '../common/constants'; +import { MagitState } from '../models/magitState'; +import { DocumentView } from './general/documentView'; +import { Uri } from 'vscode'; +import { MagitRepository } from '../models/magitRepository'; +import { TextView } from './general/textView'; + +export default class SubmoduleListView extends DocumentView { + + static UriPath: string = 'submodules.magit'; + + constructor(uri: Uri, magitState: MagitState) { + super(uri); + this.provideContent(magitState); + } + + provideContent(magitState: MagitState) { + this.subViews = [ + ...magitState.submodules.map(submodule => new TextView(`${submodule.name}\t\t${submodule.path}\t\t${submodule.url}`)), + ]; + } + + public update(state: MagitState): void { + this.provideContent(state); + this.triggerUpdate(); + } + + static encodeLocation(repository: MagitRepository): Uri { + return Uri.parse(`${Constants.MagitUriScheme}:${SubmoduleListView.UriPath}?${repository.rootUri.fsPath}`); + } +} \ No newline at end of file