mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
Add support for force push and force-with-lease
This commit is contained in:
parent
4bfc0a6cf4
commit
907a021f74
|
@ -275,16 +275,31 @@
|
|||
"title": "%command.push%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.pushForce",
|
||||
"title": "%command.pushForce%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.pushTo",
|
||||
"title": "%command.pushTo%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.pushToForce",
|
||||
"title": "%command.pushToForce%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.pushWithTags",
|
||||
"title": "%command.pushWithTags%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.pushWithTagsForce",
|
||||
"title": "%command.pushWithTagsForce%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.sync",
|
||||
"title": "%command.sync%",
|
||||
|
@ -493,14 +508,26 @@
|
|||
"command": "git.push",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pushForce",
|
||||
"when": "config.git.enabled && config.git.allowForcePush && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pushTo",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pushToForce",
|
||||
"when": "config.git.enabled && config.git.allowForcePush && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pushWithTags",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.pushWithTagsForce",
|
||||
"when": "config.git.enabled && config.git.allowForcePush && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.sync",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
|
@ -1045,6 +1072,21 @@
|
|||
"default": [],
|
||||
"scope": "window",
|
||||
"description": "%config.ignoredRepositories%"
|
||||
},
|
||||
"git.allowForcePush": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%config.allowForcePush%"
|
||||
},
|
||||
"git.useForceWithLease": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%config.useForceWithLease%"
|
||||
},
|
||||
"git.dontAskForcePushConfirmation": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%config.dontAskForcePushConfirmation%"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -39,8 +39,11 @@
|
|||
"command.pullRebase": "Pull (Rebase)",
|
||||
"command.pullFrom": "Pull from...",
|
||||
"command.push": "Push",
|
||||
"command.pushForce": "Push (Force)",
|
||||
"command.pushTo": "Push to...",
|
||||
"command.pushToForce": "Push to... (Force)",
|
||||
"command.pushWithTags": "Push With Tags",
|
||||
"command.pushWithTagsForce": "Push With Tags (Force)",
|
||||
"command.sync": "Sync",
|
||||
"command.syncRebase": "Sync (Rebase)",
|
||||
"command.publish": "Publish Branch",
|
||||
|
@ -83,10 +86,13 @@
|
|||
"config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.",
|
||||
"config.inputValidation": "Controls when to show commit message input validation.",
|
||||
"config.detectSubmodules": "Controls whether to automatically detect git submodules.",
|
||||
"colors.added": "Color for added resources.",
|
||||
"config.detectSubmodulesLimit": "Controls the limit of git submodules detected.",
|
||||
"config.alwaysSignOff": "Controls the signoff flag for all commits.",
|
||||
"config.ignoredRepositories": "List of git repositories to ignore.",
|
||||
"config.allowForcePush": "Controls whether force push (with or without lease) is enabled.",
|
||||
"config.useForceWithLease": "Controls whether force pushing uses the safer force-with-lease variant.",
|
||||
"config.dontAskForcePushConfirmation": "Controls whether to ask for confirmation before force-pushing.",
|
||||
"colors.added": "Color for added resources.",
|
||||
"colors.modified": "Color for modified resources.",
|
||||
"colors.deleted": "Color for deleted resources.",
|
||||
"colors.untracked": "Color for untracked resources.",
|
||||
|
|
5
extensions/git/src/api/git.d.ts
vendored
5
extensions/git/src/api/git.d.ts
vendored
|
@ -19,6 +19,11 @@ export const enum RefType {
|
|||
Tag
|
||||
}
|
||||
|
||||
export enum ForcePushMode {
|
||||
Force,
|
||||
ForceWithLease,
|
||||
}
|
||||
|
||||
export interface Ref {
|
||||
readonly type: RefType;
|
||||
readonly name?: string;
|
||||
|
|
|
@ -17,7 +17,7 @@ import { lstat, Stats } from 'fs';
|
|||
import * as os from 'os';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Ref, RefType, Branch, GitErrorCodes } from './api/git';
|
||||
import { Ref, RefType, Branch, GitErrorCodes, ForcePushMode } from './api/git';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
@ -154,6 +154,17 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{
|
|||
return { merge, resolved, unresolved };
|
||||
}
|
||||
|
||||
enum PushType {
|
||||
Push,
|
||||
PushTo,
|
||||
PushTags,
|
||||
}
|
||||
|
||||
interface PushOptions {
|
||||
pushType: PushType;
|
||||
forcePush?: boolean;
|
||||
}
|
||||
|
||||
export class CommandCenter {
|
||||
|
||||
private disposables: Disposable[];
|
||||
|
@ -1414,76 +1425,100 @@ export class CommandCenter {
|
|||
await repository.pullWithRebase(repository.HEAD);
|
||||
}
|
||||
|
||||
@command('git.push', { repository: true })
|
||||
async push(repository: Repository): Promise<void> {
|
||||
private async pushWithOptions(repository: Repository, pushOptions: PushOptions) {
|
||||
const remotes = repository.remotes;
|
||||
const config = workspace.getConfiguration('git');
|
||||
const forcePushMode = pushOptions.forcePush && config.get<boolean>('useForceWithLease') === true ? ForcePushMode.ForceWithLease : ForcePushMode.Force;
|
||||
|
||||
if (pushOptions.forcePush && config.get<boolean>('dontAskForcePushConfirmation') === false) {
|
||||
const message = localize('confirm force push', "You are about to force push your changes, this can be destructive and could inadvertedly overwrite changes made by others.\n\nAre you sure to continue?");
|
||||
const yes = localize('ok', "OK");
|
||||
const dontAsk = localize('dontAsk', "OK, do not ask me again");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes, dontAsk);
|
||||
|
||||
if (pick === dontAsk) {
|
||||
config.update('dontAskForcePushConfirmation', true, true);
|
||||
} else if (pick !== yes) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (remotes.length === 0) {
|
||||
window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pushOptions.pushType === PushType.PushTags) {
|
||||
await repository.pushTags(undefined, forcePushMode);
|
||||
|
||||
window.showInformationMessage(localize('push with tags success', "Successfully pushed with tags."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!repository.HEAD || !repository.HEAD.name) {
|
||||
window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote."));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await repository.push(repository.HEAD);
|
||||
} catch (err) {
|
||||
if (err.gitErrorCode !== GitErrorCodes.NoUpstreamBranch) {
|
||||
throw err;
|
||||
}
|
||||
if (pushOptions.pushType === PushType.Push) {
|
||||
try {
|
||||
await repository.push(repository.HEAD, forcePushMode);
|
||||
} catch (err) {
|
||||
if (err.gitErrorCode !== GitErrorCodes.NoUpstreamBranch) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const branchName = repository.HEAD.name;
|
||||
const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName);
|
||||
const yes = localize('ok', "OK");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
|
||||
if (pick === yes) {
|
||||
await this.publish(repository);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const branchName = repository.HEAD.name;
|
||||
const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName);
|
||||
const yes = localize('ok', "OK");
|
||||
const pick = await window.showWarningMessage(message, { modal: true }, yes);
|
||||
const picks = remotes.filter(r => r.pushUrl !== undefined).map(r => ({ label: r.name, description: r.pushUrl! }));
|
||||
const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName);
|
||||
const pick = await window.showQuickPick(picks, { placeHolder });
|
||||
|
||||
if (pick === yes) {
|
||||
await this.publish(repository);
|
||||
if (!pick) {
|
||||
return;
|
||||
}
|
||||
|
||||
await repository.pushTo(pick.label, branchName, undefined, forcePushMode);
|
||||
}
|
||||
}
|
||||
|
||||
@command('git.push', { repository: true })
|
||||
async push(repository: Repository): Promise<void> {
|
||||
await this.pushWithOptions(repository, { pushType: PushType.Push });
|
||||
}
|
||||
|
||||
@command('git.pushForce', { repository: true })
|
||||
async pushForce(repository: Repository): Promise<void> {
|
||||
await this.pushWithOptions(repository, { pushType: PushType.Push, forcePush: true });
|
||||
}
|
||||
|
||||
@command('git.pushWithTags', { repository: true })
|
||||
async pushWithTags(repository: Repository): Promise<void> {
|
||||
const remotes = repository.remotes;
|
||||
await this.pushWithOptions(repository, { pushType: PushType.PushTags });
|
||||
}
|
||||
|
||||
if (remotes.length === 0) {
|
||||
window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to."));
|
||||
return;
|
||||
}
|
||||
|
||||
await repository.pushTags();
|
||||
|
||||
window.showInformationMessage(localize('push with tags success', "Successfully pushed with tags."));
|
||||
@command('git.pushWithTagsForce', { repository: true })
|
||||
async pushWithTagsForce(repository: Repository): Promise<void> {
|
||||
await this.pushWithOptions(repository, { pushType: PushType.PushTags, forcePush: true });
|
||||
}
|
||||
|
||||
@command('git.pushTo', { repository: true })
|
||||
async pushTo(repository: Repository): Promise<void> {
|
||||
const remotes = repository.remotes;
|
||||
await this.pushWithOptions(repository, { pushType: PushType.PushTo });
|
||||
}
|
||||
|
||||
if (remotes.length === 0) {
|
||||
window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!repository.HEAD || !repository.HEAD.name) {
|
||||
window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote."));
|
||||
return;
|
||||
}
|
||||
|
||||
const branchName = repository.HEAD.name;
|
||||
const picks = remotes.filter(r => r.pushUrl !== undefined).map(r => ({ label: r.name, description: r.pushUrl! }));
|
||||
const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName);
|
||||
const pick = await window.showQuickPick(picks, { placeHolder });
|
||||
|
||||
if (!pick) {
|
||||
return;
|
||||
}
|
||||
|
||||
await repository.pushTo(pick.label, branchName);
|
||||
@command('git.pushToForce', { repository: true })
|
||||
async pushToForce(repository: Repository): Promise<void> {
|
||||
await this.pushWithOptions(repository, { pushType: PushType.PushTo, forcePush: true });
|
||||
}
|
||||
|
||||
private async _sync(repository: Repository, rebase: boolean): Promise<void> {
|
||||
|
|
|
@ -1181,9 +1181,17 @@ export class Repository {
|
|||
}
|
||||
}
|
||||
|
||||
async push(remote?: string, name?: string, setUpstream: boolean = false, tags = false): Promise<void> {
|
||||
async push(remote?: string, name?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise<void> {
|
||||
const args = ['push'];
|
||||
|
||||
if (forcePushMode) {
|
||||
if (forcePushMode === ForcePushMode.ForceWithLease) {
|
||||
args.push('--force-with-lease');
|
||||
} else if (forcePushMode === ForcePushMode.Force) {
|
||||
args.push('--force');
|
||||
}
|
||||
}
|
||||
|
||||
if (setUpstream) {
|
||||
args.push('-u');
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import * as path from 'path';
|
|||
import * as nls from 'vscode-nls';
|
||||
import * as fs from 'fs';
|
||||
import { StatusBarCommands } from './statusbar';
|
||||
import { Branch, Ref, Remote, RefType, GitErrorCodes } from './api/git';
|
||||
import { Branch, Ref, Remote, RefType, GitErrorCodes, ForcePushMode } from './api/git';
|
||||
|
||||
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
|
||||
|
||||
|
@ -895,7 +895,7 @@ export class Repository implements Disposable {
|
|||
}
|
||||
|
||||
@throttle
|
||||
async push(head: Branch): Promise<void> {
|
||||
async push(head: Branch, forcePushMode?: ForcePushMode): Promise<void> {
|
||||
let remote: string | undefined;
|
||||
let branch: string | undefined;
|
||||
|
||||
|
@ -904,15 +904,15 @@ export class Repository implements Disposable {
|
|||
branch = `${head.name}:${head.upstream.name}`;
|
||||
}
|
||||
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, branch));
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, branch, undefined, undefined, forcePushMode));
|
||||
}
|
||||
|
||||
async pushTo(remote?: string, name?: string, setUpstream: boolean = false): Promise<void> {
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream));
|
||||
async pushTo(remote?: string, name?: string, setUpstream: boolean = false, forcePushMode?: ForcePushMode): Promise<void> {
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream, undefined, forcePushMode));
|
||||
}
|
||||
|
||||
async pushTags(remote?: string): Promise<void> {
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true));
|
||||
async pushTags(remote?: string, forcePushMode?: ForcePushMode): Promise<void> {
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode));
|
||||
}
|
||||
|
||||
@throttle
|
||||
|
|
Loading…
Reference in a new issue