mirror of
https://github.com/tiktok/sparo.git
synced 2024-11-14 19:35:12 -05:00
feat: sparo fetch & pull clean up merged branch from git configuration
This commit is contained in:
parent
4dbdd16c16
commit
2875895e2b
8 changed files with 293 additions and 82 deletions
|
@ -2,11 +2,13 @@ import * as child_process from 'child_process';
|
|||
import { inject } from 'inversify';
|
||||
import { Command } from '../../decorator';
|
||||
import { GitService } from '../../services/GitService';
|
||||
import { GitRemoteFetchConfigService } from '../../services/GitRemoteFetchConfigService';
|
||||
import { TerminalService } from '../../services/TerminalService';
|
||||
import { SparoProfileService } from '../../services/SparoProfileService';
|
||||
|
||||
import type { ICommand } from './base';
|
||||
import type { ArgumentsCamelCase, Argv } from 'yargs';
|
||||
|
||||
export interface ICheckoutCommandOptions {
|
||||
profile: string[];
|
||||
branch?: string;
|
||||
|
@ -27,6 +29,7 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
'Updates files in the working tree to match the version in the index or the specified tree. If no pathspec was given, git checkout will also update HEAD to set the specified branch as the current branch.';
|
||||
|
||||
@inject(GitService) private _gitService!: GitService;
|
||||
@inject(GitRemoteFetchConfigService) private _gitRemoteFetchConfigService!: GitRemoteFetchConfigService;
|
||||
@inject(SparoProfileService) private _sparoProfileService!: SparoProfileService;
|
||||
|
||||
public builder(yargs: Argv<{}>): void {
|
||||
|
@ -119,7 +122,7 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
/**
|
||||
* Since we set up single branch by default and branch can be missing in local, we are going to fetch the branch from remote server here.
|
||||
*/
|
||||
const currentBranch: string = this._getCurrentBranch();
|
||||
const currentBranch: string = this._gitService.getCurrentBranch();
|
||||
let operationBranch: string = currentBranch;
|
||||
if (b || B) {
|
||||
operationBranch = startPoint || operationBranch;
|
||||
|
@ -266,7 +269,7 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
args: ['branch', branch, `${remote}/${branch}`]
|
||||
});
|
||||
|
||||
this._addRemoteBranchIfNotExists(remote, branch);
|
||||
this._gitRemoteFetchConfigService.addRemoteBranchIfNotExists(remote, branch);
|
||||
}
|
||||
|
||||
const branchExistsInLocal: boolean = Boolean(
|
||||
|
@ -280,15 +283,6 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
return branchExistsInLocal;
|
||||
}
|
||||
|
||||
private _getCurrentBranch(): string {
|
||||
const currentBranch: string = this._gitService
|
||||
.executeGitCommandAndCaptureOutput({
|
||||
args: ['branch', '--show-current']
|
||||
})
|
||||
.trim();
|
||||
return currentBranch;
|
||||
}
|
||||
|
||||
private _ensureTagInLocal(tag: string): boolean {
|
||||
// fetch from remote
|
||||
const remote: string = 'origin';
|
||||
|
@ -306,27 +300,4 @@ export class CheckoutCommand implements ICommand<ICheckoutCommandOptions> {
|
|||
);
|
||||
return tagExistsInLocal;
|
||||
}
|
||||
|
||||
private _addRemoteBranchIfNotExists(remote: string, branch: string): void {
|
||||
const result: string | undefined = this._gitService.getGitConfig(`remote.${remote}.fetch`, {
|
||||
array: true
|
||||
});
|
||||
const remoteFetchGitConfig: string[] | undefined = result?.split('\n').filter(Boolean);
|
||||
|
||||
if (remoteFetchGitConfig) {
|
||||
const targetConfig: string = `+refs/heads/${branch}:refs/remotes/${remote}/${branch}`;
|
||||
if (
|
||||
// Prevents adding remote branch if it is not single branch mode
|
||||
remoteFetchGitConfig.includes(`+refs/heads/*:refs/remotes/${remote}/*`) ||
|
||||
// Prevents adding the same remote branch multiple times
|
||||
remoteFetchGitConfig?.some((value: string) => value === targetConfig)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._gitService.executeGitCommand({
|
||||
args: ['remote', 'set-branches', '--add', remote, branch]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { inject } from 'inversify';
|
||||
import { Command } from '../../decorator';
|
||||
import { GitService } from '../../services/GitService';
|
||||
import { GracefulShutdownService } from '../../services/GracefulShutdownService';
|
||||
import { GitRemoteFetchConfigService } from '../../services/GitRemoteFetchConfigService';
|
||||
|
||||
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
||||
import type { GitRepoInfo } from 'git-repo-info';
|
||||
|
@ -20,7 +20,8 @@ export class FetchCommand implements ICommand<IFetchCommandOptions> {
|
|||
public description: string = 'fetch remote branch to local';
|
||||
|
||||
@inject(GitService) private _gitService!: GitService;
|
||||
@inject(GracefulShutdownService) private _gracefulShutdownService!: GracefulShutdownService;
|
||||
@inject(GitRemoteFetchConfigService) private _gitRemoteFetchConfigService!: GitRemoteFetchConfigService;
|
||||
|
||||
public builder(yargs: Argv<{}>): void {
|
||||
/**
|
||||
* sparo fetch <remote> <branch> [--all]
|
||||
|
@ -46,10 +47,12 @@ export class FetchCommand implements ICommand<IFetchCommandOptions> {
|
|||
const { all, branch = defaultBranch, remote = this._gitService.getBranchRemote(branch) } = args;
|
||||
const fetchArgs: string[] = ['fetch'];
|
||||
|
||||
await this._gitRemoteFetchConfigService.pruneRemoteBranchesInGitConfigAsync(remote || 'origin');
|
||||
|
||||
let restoreSingleBranchCallback: (() => void) | undefined;
|
||||
if (all) {
|
||||
// Temporary revert single branch fetch if necessary
|
||||
restoreSingleBranchCallback = this._revertSingleBranchIfNecessary(remote);
|
||||
restoreSingleBranchCallback = this._gitRemoteFetchConfigService.revertSingleBranchIfNecessary(remote);
|
||||
|
||||
fetchArgs.push('--all');
|
||||
} else {
|
||||
|
@ -64,46 +67,4 @@ export class FetchCommand implements ICommand<IFetchCommandOptions> {
|
|||
public getHelp(): string {
|
||||
return `fetch help`;
|
||||
}
|
||||
|
||||
private _revertSingleBranchIfNecessary = (remote: string): (() => void) | undefined => {
|
||||
let remoteFetchGitConfig: string[] | undefined = this._getRemoteFetchGitConfig(remote);
|
||||
let callback: (() => void) | undefined;
|
||||
if (remoteFetchGitConfig && !remoteFetchGitConfig.includes(`+refs/heads/*:refs/remotes/${remote}/*`)) {
|
||||
this._setAllBranchFetch(remote);
|
||||
|
||||
callback = () => {
|
||||
if (remoteFetchGitConfig) {
|
||||
this._restoreSingleBranchFetch(remote, remoteFetchGitConfig);
|
||||
|
||||
// Avoid memory leaking
|
||||
remoteFetchGitConfig = undefined;
|
||||
this._gracefulShutdownService.unregisterCallback(callback);
|
||||
}
|
||||
};
|
||||
|
||||
this._gracefulShutdownService.registerCallback(callback);
|
||||
}
|
||||
|
||||
return callback;
|
||||
};
|
||||
|
||||
private _getRemoteFetchGitConfig(remote: string): string[] | undefined {
|
||||
const result: string | undefined = this._gitService.getGitConfig(`remote.${remote}.fetch`, {
|
||||
array: true
|
||||
});
|
||||
return result?.split('\n').filter(Boolean);
|
||||
}
|
||||
|
||||
private _setAllBranchFetch(remote: string): void {
|
||||
this._gitService.setGitConfig(`remote.${remote}.fetch`, `+refs/heads/*:refs/remotes/${remote}/*`, {
|
||||
replaceAll: true
|
||||
});
|
||||
}
|
||||
|
||||
private _restoreSingleBranchFetch(remote: string, remoteFetchGitConfig: string[]): void {
|
||||
this._gitService.unsetGitConfig(`remote.${remote}.fetch`);
|
||||
for (const value of remoteFetchGitConfig) {
|
||||
this._gitService.setGitConfig(`remote.${remote}.fetch`, value, { add: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { inject } from 'inversify';
|
||||
import { Command } from '../../decorator';
|
||||
import { GitService } from '../../services/GitService';
|
||||
import { GitRemoteFetchConfigService } from '../../services/GitRemoteFetchConfigService';
|
||||
import { SparoProfileService } from '../../services/SparoProfileService';
|
||||
|
||||
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
||||
|
@ -8,15 +9,17 @@ import type { ICommand } from './base';
|
|||
import type { TerminalService } from '../../services/TerminalService';
|
||||
|
||||
export interface IPullCommandOptions {
|
||||
remote?: string;
|
||||
profile?: string[];
|
||||
}
|
||||
|
||||
@Command()
|
||||
export class PullCommand implements ICommand<IPullCommandOptions> {
|
||||
public cmd: string = 'pull';
|
||||
public cmd: string = 'pull [remote]';
|
||||
public description: string = 'Incorporates changes from a remote repository into the current branch.';
|
||||
|
||||
@inject(GitService) private _gitService!: GitService;
|
||||
@inject(GitRemoteFetchConfigService) private _gitRemoteFetchConfigService!: GitRemoteFetchConfigService;
|
||||
@inject(SparoProfileService) private _sparoProfileService!: SparoProfileService;
|
||||
|
||||
public builder = (yargs: Argv<{}>): void => {
|
||||
|
@ -28,6 +31,9 @@ export class PullCommand implements ICommand<IPullCommandOptions> {
|
|||
* sparo pull origin master
|
||||
*/
|
||||
yargs
|
||||
.positional('remote', {
|
||||
type: 'string'
|
||||
})
|
||||
.array('profile')
|
||||
.default('profile', [])
|
||||
.parserConfiguration({ 'unknown-options-as-args': true })
|
||||
|
@ -54,6 +60,13 @@ export class PullCommand implements ICommand<IPullCommandOptions> {
|
|||
addProfilesFromArg: []
|
||||
});
|
||||
|
||||
const { remote } = args;
|
||||
if (remote) {
|
||||
pullArgs.splice(1, 0, remote);
|
||||
}
|
||||
|
||||
await this._gitRemoteFetchConfigService.pruneRemoteBranchesInGitConfigAsync(remote || 'origin');
|
||||
|
||||
// invoke native git pull command
|
||||
gitService.executeGitCommand({ args: pullArgs });
|
||||
|
||||
|
|
174
apps/sparo-lib/src/services/GitRemoteFetchConfigService.ts
Normal file
174
apps/sparo-lib/src/services/GitRemoteFetchConfigService.ts
Normal file
|
@ -0,0 +1,174 @@
|
|||
import { inject } from 'inversify';
|
||||
import { Colorize } from '@rushstack/terminal';
|
||||
|
||||
import { Service } from '../decorator';
|
||||
import { GitService } from './GitService';
|
||||
import { TerminalService } from './TerminalService';
|
||||
import { GracefulShutdownService } from './GracefulShutdownService';
|
||||
|
||||
/**
|
||||
* Helper class for git remote.origin.fetch config
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
@Service()
|
||||
export class GitRemoteFetchConfigService {
|
||||
@inject(GitService) private _gitService!: GitService;
|
||||
@inject(TerminalService) private _terminalService!: TerminalService;
|
||||
@inject(GracefulShutdownService) private _gracefulShutdownService!: GracefulShutdownService;
|
||||
|
||||
public addRemoteBranchIfNotExists(remote: string, branch: string): void {
|
||||
const remoteFetchGitConfig: string[] | undefined = this._loadForRemote(remote);
|
||||
|
||||
if (remoteFetchGitConfig) {
|
||||
const targetConfig: string = `+refs/heads/${branch}:refs/remotes/${remote}/${branch}`;
|
||||
if (
|
||||
// Prevents adding remote branch if it is not single branch mode
|
||||
remoteFetchGitConfig.includes(`+refs/heads/*:refs/remotes/${remote}/*`) ||
|
||||
// Prevents adding the same remote branch multiple times
|
||||
remoteFetchGitConfig?.some((value: string) => value === targetConfig)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._gitService.executeGitCommand({
|
||||
args: ['remote', 'set-branches', '--add', remote, branch]
|
||||
});
|
||||
}
|
||||
|
||||
public pruneRemoteBranchesInGitConfigAsync = async (remote: string): Promise<void> => {
|
||||
const remoteFetchConfig: string[] | undefined = this._loadForRemote(remote);
|
||||
if (!remoteFetchConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
const invalidRemoteFetchConfig: string[] = [];
|
||||
const invalidBranches: string[] = [];
|
||||
const branchToValues: Map<string, Set<string>> = this.getBranchesInfoFromRemoteFetchConfig(
|
||||
remoteFetchConfig
|
||||
);
|
||||
const checkBranches: string[] = Array.from(branchToValues.keys()).filter((x) => x !== '*');
|
||||
|
||||
const remoteBranchExistenceInfo: Record<string, boolean> =
|
||||
await this._gitService.checkRemoteBranchesExistenceAsync(remote, checkBranches);
|
||||
|
||||
for (const [branch, isExists] of Object.entries(remoteBranchExistenceInfo)) {
|
||||
if (isExists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
invalidBranches.push(branch);
|
||||
|
||||
const remoteFetchConfigValues: Set<string> | undefined = branchToValues.get(branch);
|
||||
if (remoteFetchConfigValues) {
|
||||
invalidRemoteFetchConfig.push(...remoteFetchConfigValues);
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidRemoteFetchConfig.length) {
|
||||
for (const invalidBranch of invalidBranches) {
|
||||
this._terminalService.terminal.writeLine(
|
||||
Colorize.gray(
|
||||
`Branch "${invalidBranch}" doesn't exist remotely. It might have been merged into the main branch. Pruning this branch from the git configuration.`
|
||||
)
|
||||
);
|
||||
}
|
||||
const nextRemoteFetchConfigSet: Set<string> = new Set<string>(remoteFetchConfig);
|
||||
this._terminalService.terminal.writeDebugLine(
|
||||
`Pruning the following value(s) in remote.${remote}.fetch from git configuration`
|
||||
);
|
||||
for (const invalidValue of invalidRemoteFetchConfig) {
|
||||
this._terminalService.terminal.writeDebugLine(invalidValue);
|
||||
nextRemoteFetchConfigSet.delete(invalidValue);
|
||||
}
|
||||
|
||||
// Restores previous git configuration if something went wrong
|
||||
const callback = (): void => {
|
||||
this._setRemoteFetchInGitConfig(remote, remoteFetchConfig);
|
||||
this._terminalService.terminal.writeDebugLine(
|
||||
`Restore previous remote.${remote}.fetch to git configuration`
|
||||
);
|
||||
};
|
||||
|
||||
this._gracefulShutdownService.registerCallback(callback);
|
||||
this._setRemoteFetchInGitConfig(remote, Array.from(nextRemoteFetchConfigSet));
|
||||
this._gracefulShutdownService.unregisterCallback(callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sparo uses single branch mode as default. This function switch to all branch mode from single branch mode.
|
||||
* And, it returns a callback function to go back to single branch mode with previous git configuration.
|
||||
* It's used in "sparo fetch --all" command
|
||||
*/
|
||||
public revertSingleBranchIfNecessary = (remote: string): (() => void) | undefined => {
|
||||
let remoteFetchGitConfig: string[] | undefined = this._loadForRemote(remote);
|
||||
let callback: (() => void) | undefined;
|
||||
if (remoteFetchGitConfig && !remoteFetchGitConfig.includes(`+refs/heads/*:refs/remotes/${remote}/*`)) {
|
||||
this._setAllBranchFetch(remote);
|
||||
|
||||
callback = () => {
|
||||
if (remoteFetchGitConfig) {
|
||||
this._setRemoteFetchInGitConfig(remote, remoteFetchGitConfig);
|
||||
|
||||
// Avoid memory leaking
|
||||
remoteFetchGitConfig = undefined;
|
||||
this._gracefulShutdownService.unregisterCallback(callback);
|
||||
}
|
||||
};
|
||||
|
||||
this._gracefulShutdownService.registerCallback(callback);
|
||||
}
|
||||
|
||||
return callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads remote.origin.fetch from git configuration. It returns a mapping
|
||||
*/
|
||||
public getBranchesInfoFromRemoteFetchConfig(remoteFetchConfig: string[]): Map<string, Set<string>> {
|
||||
const branchRegExp: RegExp = /^(?:\+)?refs\/heads\/([^:]+):/;
|
||||
const branchToValues: Map<string, Set<string>> = new Map<string, Set<string>>();
|
||||
for (const remoteFetchConfigValue of remoteFetchConfig) {
|
||||
const match: RegExpMatchArray | null = remoteFetchConfigValue.match(branchRegExp);
|
||||
if (match) {
|
||||
const branch: string | undefined = match[1];
|
||||
if (branch) {
|
||||
let values: Set<string> | undefined = branchToValues.get(branch);
|
||||
if (!values) {
|
||||
values = new Set<string>();
|
||||
branchToValues.set(branch, values);
|
||||
}
|
||||
values.add(remoteFetchConfigValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return branchToValues;
|
||||
}
|
||||
|
||||
private _loadForRemote(remote: string): string[] | undefined {
|
||||
const result: string | undefined = this._gitService.getGitConfig(`remote.${remote}.fetch`, {
|
||||
array: true
|
||||
});
|
||||
const remoteFetchGitConfig: string[] | undefined = result?.split('\n').filter(Boolean);
|
||||
return remoteFetchGitConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no easy way to unset one branch from git configuration
|
||||
* So, delete all remote.origin.fetch configuration and restores expected value
|
||||
*/
|
||||
private _setRemoteFetchInGitConfig(remote: string, remoteFetchGitConfig: string[]): void {
|
||||
this._gitService.unsetGitConfig(`remote.${remote}.fetch`);
|
||||
for (const value of remoteFetchGitConfig) {
|
||||
this._gitService.setGitConfig(`remote.${remote}.fetch`, value, { add: true });
|
||||
}
|
||||
}
|
||||
|
||||
private _setAllBranchFetch(remote: string): void {
|
||||
this._gitService.setGitConfig(`remote.${remote}.fetch`, `+refs/heads/*:refs/remotes/${remote}/*`, {
|
||||
replaceAll: true
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import * as child_process from 'child_process';
|
||||
import { Executable } from '@rushstack/node-core-library';
|
||||
import { Async, Executable } from '@rushstack/node-core-library';
|
||||
import getRepoInfo, { type GitRepoInfo } from 'git-repo-info';
|
||||
import { inject } from 'inversify';
|
||||
import { Service } from '../decorator';
|
||||
|
@ -438,6 +438,66 @@ Please specify a directory on the command line
|
|||
return objectType;
|
||||
}
|
||||
|
||||
public getCurrentBranch(): string {
|
||||
const currentBranch: string = this.executeGitCommandAndCaptureOutput({
|
||||
args: ['branch', '--show-current']
|
||||
}).trim();
|
||||
return currentBranch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check existence for a list of branch name
|
||||
*/
|
||||
public checkRemoteBranchesExistenceAsync = async (
|
||||
remote: string,
|
||||
branches: string[]
|
||||
): Promise<Record<string, boolean>> => {
|
||||
this._terminalService.terminal.writeDebugLine(`Checking branches: ${branches.join(',')}`);
|
||||
const ret: Record<string, boolean> = {};
|
||||
await Async.forEachAsync(branches, async (branch: string) => {
|
||||
const isExists: boolean = await this.checkRemoteBranchExistenceAsync(remote, branch);
|
||||
ret[branch] = isExists;
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check existence for one branch name.
|
||||
*
|
||||
* {@link checkRemoteBranchesExistenceAsync} is preferred if you are going to check a list of branch name.
|
||||
*/
|
||||
public checkRemoteBranchExistenceAsync = async (remote: string, branch: string): Promise<boolean> => {
|
||||
const gitPath: string = this.getGitPathOrThrow();
|
||||
const currentWorkingDirectory: string = this.getRepoInfo().root;
|
||||
const childProcess: child_process.ChildProcess = Executable.spawn(
|
||||
gitPath,
|
||||
['ls-remote', '--exit-code', remote, branch],
|
||||
{
|
||||
currentWorkingDirectory,
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
}
|
||||
);
|
||||
if (!childProcess.stdout || !childProcess.stderr) {
|
||||
this._terminalService.terminal.writeDebugLine(`Failed to spawn git process, fallback to spawnSync`);
|
||||
const result: string = this.executeGitCommandAndCaptureOutput({
|
||||
args: ['ls-remote', remote, branch]
|
||||
}).trim();
|
||||
return Promise.resolve(!!result);
|
||||
}
|
||||
return await new Promise((resolve, reject) => {
|
||||
// Only care about exit code since specifying --exit-code
|
||||
childProcess.on('close', (exitCode: number | null) => {
|
||||
if (exitCode) {
|
||||
this._terminalService.terminal.writeDebugLine(`Branch "${branch}" doesn't exist remotely`);
|
||||
resolve(false);
|
||||
} else {
|
||||
this._terminalService.terminal.writeDebugLine(`Branch "${branch}" exists remotely`);
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private _processResult(result: child_process.SpawnSyncReturns<string>): void {
|
||||
if (result.error) {
|
||||
result.error.message += '\n' + (result.stderr ? result.stderr.toString() + '\n' : '');
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { getFromContainer } from '../../di/container';
|
||||
import { GitRemoteFetchConfigService } from '../GitRemoteFetchConfigService';
|
||||
|
||||
describe(GitRemoteFetchConfigService.name, () => {
|
||||
const gitRemoteFetchConfigService = getFromContainer(GitRemoteFetchConfigService);
|
||||
|
||||
describe(gitRemoteFetchConfigService.getBranchesInfoFromRemoteFetchConfig, () => {
|
||||
it('should work', () => {
|
||||
const values: string[] = [
|
||||
'+refs/heads/*:refs/remotes/origin/*',
|
||||
'+refs/heads/release:refs/remotes/origin/release',
|
||||
'+refs/heads/feat/abc:refs/remotes/origin/feat/abc'
|
||||
];
|
||||
|
||||
const expectedContaining: Record<string, string> = {
|
||||
'*': values[0],
|
||||
release: values[1],
|
||||
'feat/abc': values[2]
|
||||
};
|
||||
|
||||
for (const [k, v] of Object.entries(
|
||||
gitRemoteFetchConfigService.getBranchesInfoFromRemoteFetchConfig(values)
|
||||
)) {
|
||||
expect(v).toBe(expect.arrayContaining([expectedContaining[k]]));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -20,7 +20,7 @@ Commands:
|
|||
HEAD to set the specified branch as the
|
||||
current branch.
|
||||
sparo fetch [remote] [branch] fetch remote branch to local
|
||||
sparo pull Incorporates changes from a remote
|
||||
sparo pull [remote] Incorporates changes from a remote
|
||||
repository into the current branch.
|
||||
sparo git-clone original git clone command
|
||||
sparo git-checkout original git checkout command
|
||||
|
|
|
@ -17,6 +17,8 @@ export function getFromContainerAsync<T>(clazz: Constructable<T>): Promise<T>;
|
|||
|
||||
// @alpha
|
||||
export class GitService {
|
||||
checkRemoteBranchesExistenceAsync: (remote: string, branches: string[]) => Promise<Record<string, boolean>>;
|
||||
checkRemoteBranchExistenceAsync: (remote: string, branch: string) => Promise<boolean>;
|
||||
// (undocumented)
|
||||
executeGitCommand({ args, workingDirectory }: IExecuteGitCommandParams): child_process.SpawnSyncReturns<string>;
|
||||
// (undocumented)
|
||||
|
@ -25,6 +27,8 @@ export class GitService {
|
|||
// (undocumented)
|
||||
getBranchRemote(branch: string): string;
|
||||
// (undocumented)
|
||||
getCurrentBranch(): string;
|
||||
// (undocumented)
|
||||
getGitConfig(k: string, option?: {
|
||||
dryRun?: boolean;
|
||||
global?: boolean;
|
||||
|
|
Loading…
Reference in a new issue