diff --git a/extensions/git/package.json b/extensions/git/package.json index ea99da5140a39..b0b11e653a7db 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -150,6 +150,12 @@ "category": "Git", "icon": "$(discard)" }, + { + "command": "git.rename", + "title": "%command.rename%", + "category": "Git", + "icon": "$(discard)" + }, { "command": "git.cleanAll", "title": "%command.cleanAll%", @@ -1296,6 +1302,13 @@ "group": "5_copy@2", "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/" } + ], + "explorer/context": [ + { + "command": "git.rename", + "group": "7_modification", + "when": "config.git.enabled && !git.missing && !explorerResourceIsRoot" + } ] }, "configuration": { diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 39965be4f75c5..eba9ea7eefc1e 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -21,6 +21,7 @@ "command.unstage": "Unstage Changes", "command.unstageAll": "Unstage All Changes", "command.unstageSelectedRanges": "Unstage Selected Ranges", + "command.rename": "Rename (Git)", "command.clean": "Discard Changes", "command.cleanAll": "Discard All Changes", "command.cleanAllTracked": "Discard All Tracked Changes", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index d6eeeec88ba30..c370133d8d7b2 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -930,6 +930,26 @@ export class CommandCenter { } } + @command('git.rename', { repository: true }) + async rename(repository: Repository, fromUri: Uri): Promise { + this.outputChannel.appendLine(`git.rename ${fromUri.fsPath}`); + + const rootPath = workspace.rootPath; + const fromPath = workspace.asRelativePath(fromUri); + const fromBasename = path.basename(fromPath); + const toPath = await window.showInputBox({ + value: fromPath, + valueSelection: [fromPath.length - fromBasename.length, fromPath.length] + }); + if (!toPath?.trim()) { + return; + } + + const fullToPath = path.join(rootPath || '', toPath); + this.outputChannel.appendLine(`git.rename from ${fromPath} to ${fullToPath}`); + await repository.move(fromPath, fullToPath); + } + @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { this.outputChannel.appendLine(`git.stage ${resourceStates.length}`); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 1b5c8d4837162..8323ef3a45915 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1390,6 +1390,11 @@ export class Repository { await this.run(args); } + async move(from: string, to: string): Promise { + const args = ['mv', from, to]; + await this.run(args); + } + async setBranchUpstream(name: string, upstream: string): Promise { const args = ['branch', '--set-upstream-to', upstream, name]; await this.run(args); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 9e85d0fca0c38..7466cfd811230 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -310,6 +310,8 @@ export const enum Operation { Blame = 'Blame', Log = 'Log', LogFile = 'LogFile', + + Move = 'Move' } function isReadOnly(operation: Operation): boolean { @@ -1045,6 +1047,10 @@ export class Repository implements Disposable { await this.run(Operation.RenameBranch, () => this.repository.renameBranch(name)); } + async move(from: string, to: string): Promise { + await this.run(Operation.Move, () => this.repository.move(from, to)); + } + async getBranch(name: string): Promise { return await this.run(Operation.GetBranch, () => this.repository.getBranch(name)); }