Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions extensions/git/src/api/api1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ export class ApiRepository implements Repository {
migrateChanges(sourceRepositoryPath: string, options?: { confirmation?: boolean; deleteFromSource?: boolean; untracked?: boolean }): Promise<void> {
return this.#repository.migrateChanges(sourceRepositoryPath, options);
}

generateRandomBranchName(): Promise<string | undefined> {
return this.#repository.generateRandomBranchName();
}
}

export class ApiGit implements Git {
Expand Down
2 changes: 2 additions & 0 deletions extensions/git/src/api/git.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ export interface Repository {
deleteWorktree(path: string, options?: { force?: boolean }): Promise<void>;

migrateChanges(sourceRepositoryPath: string, options?: { confirmation?: boolean; deleteFromSource?: boolean; untracked?: boolean }): Promise<void>;

generateRandomBranchName(): Promise<string | undefined>;
}

export interface RemoteSource {
Expand Down
46 changes: 1 addition & 45 deletions extensions/git/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import * as os from 'os';
import * as path from 'path';
import { Command, commands, Disposable, MessageOptions, Position, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation, languages, SourceControlArtifact, ProgressLocation } from 'vscode';
import TelemetryReporter from '@vscode/extension-telemetry';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import type { CommitOptions, RemoteSourcePublisher, Remote, Branch, Ref } from './api/git';
import { ForcePushMode, GitErrorCodes, RefType, Status } from './api/git.constants';
import { Git, GitError, Repository as GitRepository, Stash, Worktree } from './git';
Expand Down Expand Up @@ -2943,48 +2942,6 @@ export class CommandCenter {
await this._branch(repository, undefined, true);
}

private async generateRandomBranchName(repository: Repository, separator: string): Promise<string> {
const config = workspace.getConfiguration('git');
const branchRandomNameDictionary = config.get<string[]>('branchRandomName.dictionary')!;

const dictionaries: string[][] = [];
for (const dictionary of branchRandomNameDictionary) {
if (dictionary.toLowerCase() === 'adjectives') {
dictionaries.push(adjectives);
}
if (dictionary.toLowerCase() === 'animals') {
dictionaries.push(animals);
}
if (dictionary.toLowerCase() === 'colors') {
dictionaries.push(colors);
}
if (dictionary.toLowerCase() === 'numbers') {
dictionaries.push(NumberDictionary.generate({ length: 3 }));
}
}

if (dictionaries.length === 0) {
return '';
}

// 5 attempts to generate a random branch name
for (let index = 0; index < 5; index++) {
const randomName = uniqueNamesGenerator({
dictionaries,
length: dictionaries.length,
separator
});

// Check for local ref conflict
const refs = await repository.getRefs({ pattern: `refs/heads/${randomName}` });
if (refs.length === 0) {
return randomName;
}
}

return '';
}

private async promptForBranchName(repository: Repository, defaultName?: string, initialValue?: string): Promise<string> {
const config = workspace.getConfiguration('git');
const branchPrefix = config.get<string>('branchPrefix')!;
Expand All @@ -2998,8 +2955,7 @@ export class CommandCenter {
}

const getBranchName = async (): Promise<string> => {
const branchName = branchRandomNameEnabled ? await this.generateRandomBranchName(repository, branchWhitespaceChar) : '';
return `${branchPrefix}${branchName}`;
return await repository.generateRandomBranchName() ?? branchPrefix;
};

const getValueSelection = (value: string): [number, number] | undefined => {
Expand Down
51 changes: 51 additions & 0 deletions extensions/git/src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import TelemetryReporter from '@vscode/extension-telemetry';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import * as fs from 'fs';
import * as fsPromises from 'fs/promises';
import * as path from 'path';
Expand Down Expand Up @@ -3294,6 +3295,56 @@ export class Repository implements Disposable {
return this.unpublishedCommits;
}

async generateRandomBranchName(): Promise<string | undefined> {
const config = workspace.getConfiguration('git', Uri.file(this.root));
const branchRandomNameEnabled = config.get<boolean>('branchRandomName.enable', false);

if (!branchRandomNameEnabled) {
return undefined;
}

const branchPrefix = config.get<string>('branchPrefix', '');
const branchWhitespaceChar = config.get<string>('branchWhitespaceChar', '-');
const branchRandomNameDictionary = config.get<string[]>('branchRandomName.dictionary', ['adjectives', 'animals']);

const dictionaries: string[][] = [];
for (const dictionary of branchRandomNameDictionary) {
if (dictionary.toLowerCase() === 'adjectives') {
dictionaries.push(adjectives);
}
if (dictionary.toLowerCase() === 'animals') {
dictionaries.push(animals);
}
if (dictionary.toLowerCase() === 'colors') {
dictionaries.push(colors);
}
if (dictionary.toLowerCase() === 'numbers') {
dictionaries.push(NumberDictionary.generate({ length: 3 }));
}
}

if (dictionaries.length === 0) {
return undefined;
}

// 5 attempts to generate a random branch name
for (let index = 0; index < 5; index++) {
const randomName = uniqueNamesGenerator({
dictionaries,
length: dictionaries.length,
separator: branchWhitespaceChar
});

// Check for local ref conflict
const refs = await this.getRefs({ pattern: `refs/heads/${branchPrefix}${randomName}` });
if (refs.length === 0) {
return `${branchPrefix}${randomName}`;
}
}

return undefined;
}

dispose(): void {
this.disposables = dispose(this.disposables);
}
Expand Down
6 changes: 4 additions & 2 deletions extensions/git/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -867,10 +867,12 @@ export function getStashDescription(stash: Stash): string | undefined {
return descriptionSegments.join(' \u2022 ');
}

export const CopilotWorktreeBranchPrefix = 'copilot-worktree-';

export function isCopilotWorktree(path: string): boolean {
const lastSepIndex = path.lastIndexOf(sep);

return lastSepIndex !== -1
? path.substring(lastSepIndex + 1).startsWith('copilot-worktree-')
: path.startsWith('copilot-worktree-');
? path.substring(lastSepIndex + 1).startsWith(CopilotWorktreeBranchPrefix)
: path.startsWith(CopilotWorktreeBranchPrefix);
}
Loading