Browse Source

Adds setUpstream ability to push

main
Eric Amodio 4 years ago
parent
commit
a7e6e4112a
4 changed files with 148 additions and 22 deletions
  1. +69
    -3
      src/@types/git.d.ts
  2. +49
    -10
      src/commands/git/push.ts
  3. +4
    -1
      src/git/gitService.ts
  4. +26
    -8
      src/git/models/repository.ts

+ 69
- 3
src/@types/git.d.ts View File

@ -3,7 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Event, Uri } from 'vscode';
import { Disposable, Event, ProviderResult, Uri } from 'vscode';
export { ProviderResult } from 'vscode';
export interface Git { export interface Git {
readonly path: string; readonly path: string;
@ -41,7 +43,10 @@ export interface Commit {
readonly hash: string; readonly hash: string;
readonly message: string; readonly message: string;
readonly parents: string[]; readonly parents: string[];
readonly authorEmail?: string | undefined;
readonly authorDate?: Date;
readonly authorName?: string;
readonly authorEmail?: string;
readonly commitDate?: Date;
} }
export interface Submodule { export interface Submodule {
@ -117,6 +122,22 @@ export interface RepositoryUIState {
export interface LogOptions { export interface LogOptions {
/** Max number of log entries to retrieve. If not specified, the default is 32. */ /** Max number of log entries to retrieve. If not specified, the default is 32. */
readonly maxEntries?: number; readonly maxEntries?: number;
readonly path?: string;
}
export interface CommitOptions {
all?: boolean | 'tracked';
amend?: boolean;
signoff?: boolean;
signCommit?: boolean;
empty?: boolean;
}
export interface BranchQuery {
readonly remote?: boolean;
readonly pattern?: string;
readonly count?: number;
readonly contains?: string;
} }
export interface Repository { export interface Repository {
@ -158,6 +179,7 @@ export interface Repository {
createBranch(name: string, checkout: boolean, ref?: string): Promise<void>; createBranch(name: string, checkout: boolean, ref?: string): Promise<void>;
deleteBranch(name: string, force?: boolean): Promise<void>; deleteBranch(name: string, force?: boolean): Promise<void>;
getBranch(name: string): Promise<Branch>; getBranch(name: string): Promise<Branch>;
getBranches(query: BranchQuery): Promise<Ref[]>;
setBranchUpstream(name: string, upstream: string): Promise<void>; setBranchUpstream(name: string, upstream: string): Promise<void>;
getMergeBase(ref1: string, ref2: string): Promise<string>; getMergeBase(ref1: string, ref2: string): Promise<string>;
@ -167,6 +189,7 @@ export interface Repository {
addRemote(name: string, url: string): Promise<void>; addRemote(name: string, url: string): Promise<void>;
removeRemote(name: string): Promise<void>; removeRemote(name: string): Promise<void>;
renameRemote(name: string, newName: string): Promise<void>;
fetch(remote?: string, ref?: string, depth?: number): Promise<void>; fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
pull(unshallow?: boolean): Promise<void>; pull(unshallow?: boolean): Promise<void>;
@ -174,13 +197,54 @@ export interface Repository {
blame(path: string): Promise<string>; blame(path: string): Promise<string>;
log(options?: LogOptions): Promise<Commit[]>; log(options?: LogOptions): Promise<Commit[]>;
commit(message: string, opts?: CommitOptions): Promise<void>;
} }
export interface RemoteSource {
readonly name: string;
readonly description?: string;
readonly url: string | string[];
}
export interface RemoteSourceProvider {
readonly name: string;
readonly icon?: string; // codicon name
readonly supportsQuery?: boolean;
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
publishRepository?(repository: Repository): Promise<void>;
}
export interface Credentials {
readonly username: string;
readonly password: string;
}
export interface CredentialsProvider {
getCredentials(host: Uri): ProviderResult<Credentials>;
}
export interface PushErrorHandler {
handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise<boolean>;
}
export type APIState = 'uninitialized' | 'initialized';
export interface API { export interface API {
readonly state: APIState;
readonly onDidChangeState: Event<APIState>;
readonly git: Git; readonly git: Git;
readonly repositories: Repository[]; readonly repositories: Repository[];
readonly onDidOpenRepository: Event<Repository>; readonly onDidOpenRepository: Event<Repository>;
readonly onDidCloseRepository: Event<Repository>; readonly onDidCloseRepository: Event<Repository>;
toGitUri(uri: Uri, ref: string): Uri;
getRepository(uri: Uri): Repository | null;
init(root: Uri): Promise<Repository | null>;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
registerPushErrorHandler(handler: PushErrorHandler): Disposable;
} }
export interface GitExtension { export interface GitExtension {
@ -218,6 +282,7 @@ export const enum GitErrorCodes {
CantOpenResource = 'CantOpenResource', CantOpenResource = 'CantOpenResource',
GitNotFound = 'GitNotFound', GitNotFound = 'GitNotFound',
CantCreatePipe = 'CantCreatePipe', CantCreatePipe = 'CantCreatePipe',
PermissionDenied = 'PermissionDenied',
CantAccessRemote = 'CantAccessRemote', CantAccessRemote = 'CantAccessRemote',
RepositoryNotFound = 'RepositoryNotFound', RepositoryNotFound = 'RepositoryNotFound',
RepositoryIsLocked = 'RepositoryIsLocked', RepositoryIsLocked = 'RepositoryIsLocked',
@ -234,5 +299,6 @@ export const enum GitErrorCodes {
CantLockRef = 'CantLockRef', CantLockRef = 'CantLockRef',
CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', CantRebaseMultipleBranches = 'CantRebaseMultipleBranches',
PatchDoesNotApply = 'PatchDoesNotApply', PatchDoesNotApply = 'PatchDoesNotApply',
NoPathFound = 'NoPathFound'
NoPathFound = 'NoPathFound',
UnknownPath = 'UnknownPath',
} }

+ 49
- 10
src/commands/git/push.ts View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { configuration } from '../../configuration'; import { configuration } from '../../configuration';
import { Container } from '../../container'; import { Container } from '../../container';
import { GitReference, Repository } from '../../git/git';
import { GitBranchReference, GitReference, Repository } from '../../git/git';
import { import {
appendReposToTitle, appendReposToTitle,
PartialStepState, PartialStepState,
@ -25,7 +25,7 @@ interface Context {
title: string; title: string;
} }
type Flags = '--force';
type Flags = '--force' | '--set-upstream' | string;
interface State<Repos = string | string[] | Repository | Repository[]> { interface State<Repos = string | string[] | Repository | Repository[]> {
repos: Repos; repos: Repos;
@ -60,8 +60,15 @@ export class PushGitCommand extends QuickCommand {
} }
execute(state: State<Repository[]>) { execute(state: State<Repository[]>) {
let setUpstream: { branch: string; remote: string } | undefined;
if (state.flags.includes('--set-upstream')) {
const index = state.flags.indexOf('--set-upstream');
setUpstream = { branch: state.flags[index + 1], remote: state.flags[index + 2] };
}
return Container.git.pushAll(state.repos, { return Container.git.pushAll(state.repos, {
force: state.flags.includes('--force'), force: state.flags.includes('--force'),
setUpstream: setUpstream,
reference: state.reference, reference: state.reference,
}); });
} }
@ -159,14 +166,46 @@ export class PushGitCommand extends QuickCommand {
const status = await repo.getStatus(); const status = await repo.getStatus();
if (status?.state.ahead === 0) { if (status?.state.ahead === 0) {
step = this.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
[],
DirectiveQuickPickItem.create(Directive.Cancel, true, {
label: `Cancel ${this.title}`,
detail: 'No commits found to push',
}),
);
const items: FlagsQuickPickItem<Flags>[] = [];
if (state.reference == null && status.upstream == null) {
const branchRef: GitBranchReference = {
refType: 'branch',
name: status.branch,
ref: status.branch,
remote: false,
repoPath: status.repoPath,
};
for (const remote of await repo.getRemotes()) {
items.push(
FlagsQuickPickItem.create<Flags>(
state.flags,
['--set-upstream', status.branch, remote.name],
{
label: `${this.title} to ${remote.name}`,
detail: `Will push ${GitReference.toString(branchRef)} to ${remote.name}`,
},
),
);
}
}
if (items.length) {
step = this.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
items,
);
} else {
step = this.createConfirmStep(
appendReposToTitle(`Confirm ${context.title}`, state, context),
[],
DirectiveQuickPickItem.create(Directive.Cancel, true, {
label: `Cancel ${this.title}`,
detail: 'No commits found to push',
}),
);
}
} else { } else {
let lastFetchedOn = ''; let lastFetchedOn = '';

+ 4
- 1
src/git/gitService.ts View File

@ -622,7 +622,10 @@ export class GitService implements Disposable {
0: (repos?: Repository[]) => (repos == null ? false : repos.map(r => r.name).join(', ')), 0: (repos?: Repository[]) => (repos == null ? false : repos.map(r => r.name).join(', ')),
}, },
}) })
async pushAll(repositories?: Repository[], options: { force?: boolean; reference?: GitReference } = {}) {
async pushAll(
repositories?: Repository[],
options: { force?: boolean; reference?: GitReference; setUpstream?: { branch: string; remote: string } } = {},
) {
if (repositories == null) { if (repositories == null) {
repositories = await this.getOrderedRepositories(); repositories = await this.getOrderedRepositories();
} }

+ 26
- 8
src/git/models/repository.ts View File

@ -445,22 +445,40 @@ export class Repository implements Disposable {
@gate() @gate()
@log() @log()
async push(options: { force?: boolean; progress?: boolean; reference?: GitReference } = {}) {
const { force, progress, reference } = { progress: true, ...options };
if (!progress) return this.pushCore(force, reference);
async push(
options: {
force?: boolean;
progress?: boolean;
reference?: GitReference;
setUpstream?: { branch: string; remote: string };
} = {},
) {
const { progress, ...opts } = { progress: true, ...options };
if (!progress) return this.pushCore(opts);
return void (await window.withProgress( return void (await window.withProgress(
{ {
location: ProgressLocation.Notification, location: ProgressLocation.Notification,
title: `Pushing ${this.formattedName}...`, title: `Pushing ${this.formattedName}...`,
}, },
() => this.pushCore(force, reference),
() => this.pushCore(opts),
)); ));
} }
private async pushCore(force: boolean = false, reference?: GitReference) {
private async pushCore(
options: {
force?: boolean;
reference?: GitReference;
setUpstream?: { branch: string; remote: string };
} = {},
) {
try { try {
if (reference != null) {
if (options.setUpstream != null) {
const repo = await GitService.getBuiltInGitRepository(this.path);
if (repo == null) return;
await repo?.push(options.setUpstream.remote, options.setUpstream.branch, true);
} else if (options.reference != null) {
const branch = await this.getBranch(); const branch = await this.getBranch();
if (branch === undefined) return; if (branch === undefined) return;
@ -469,11 +487,11 @@ export class Repository implements Disposable {
await repo?.push( await repo?.push(
branch.getRemoteName(), branch.getRemoteName(),
`${reference.ref}:${branch.getNameWithoutRemote()}`,
`${options.reference.ref}:${branch.getNameWithoutRemote()}`,
undefined, undefined,
); );
} else { } else {
void (await commands.executeCommand(force ? 'git.pushForce' : 'git.push', this.path));
void (await commands.executeCommand(options.force ? 'git.pushForce' : 'git.push', this.path));
} }
this.fireChange(RepositoryChange.Repository); this.fireChange(RepositoryChange.Repository);

Loading…
Cancel
Save