Git - branch prefix + random name generation (#149069)

This commit is contained in:
Ladislau Szomoru 2022-05-10 17:14:52 +02:00 committed by GitHub
parent e5b5dc2417
commit b44e3caa96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 25 deletions

View file

@ -1777,6 +1777,11 @@
"markdownDescription": "%config.autofetchPeriod%",
"default": 180
},
"git.branchPrefix": {
"type": "string",
"description": "%config.branchPrefix%",
"default": ""
},
"git.branchValidationRegex": {
"type": "string",
"description": "%config.branchValidationRegex%",
@ -1787,6 +1792,22 @@
"description": "%config.branchWhitespaceChar%",
"default": "-"
},
"git.branchRandomName.enable": {
"type": "boolean",
"description": "%config.branchRandomNameEnable%",
"default": false
},
"git.branchRandomName.dictionary": {
"type": "array",
"markdownDescription": "%config.branchRandomNameDictionary%",
"items": {
"type": "string"
},
"default": [
"adjectives",
"animals"
]
},
"git.confirmSync": {
"type": "boolean",
"description": "%config.confirmSync%",
@ -2503,6 +2524,7 @@
]
},
"dependencies": {
"@joaomoreno/unique-names-generator": "5.0.0",
"@vscode/extension-telemetry": "0.4.10",
"@vscode/iconv-lite-umd": "0.7.0",
"byline": "^5.0.0",

View file

@ -116,8 +116,11 @@
"config.checkoutType.local": "Local branches",
"config.checkoutType.tags": "Tags",
"config.checkoutType.remote": "Remote branches",
"config.branchPrefix": "Prefix used when creating a new branch.",
"config.branchRandomNameDictionary": "List of dictionaries used when the branch name that is randomly generated. Supported values: `adjectives`, `animals`, `colors`, `numbers`.",
"config.branchRandomNameEnable": "Controls whether a random name is generated when creating a new branch.",
"config.branchValidationRegex": "A regular expression to validate new branch names.",
"config.branchWhitespaceChar": "The character to replace whitespace in new branch names.",
"config.branchWhitespaceChar": "The character to replace whitespace in new branch names, and to separate segments of a randomly generated branch name.",
"config.ignoreLegacyWarning": "Ignores the legacy Git warning.",
"config.ignoreMissingGitWarning": "Ignores the warning when Git is missing.",
"config.ignoreWindowsGit27Warning": "Ignores the warning when Git 2.25 - 2.26 is installed on Windows.",

View file

@ -8,6 +8,7 @@ import * as path from 'path';
import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity } from 'vscode';
import TelemetryReporter from '@vscode/extension-telemetry';
import * as nls from 'vscode-nls';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher } from './api/git';
import { Git, Stash } from './git';
import { Model } from './model';
@ -1772,43 +1773,100 @@ export class CommandCenter {
await this._branch(repository, undefined, true);
}
private async promptForBranchName(defaultName?: string, initialValue?: string): Promise<string> {
private generateRandomBranchName(repository: Repository, separator: string): 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
if (!repository.refs.find(r => r.type === RefType.Head && r.name === randomName)) {
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')!;
const branchWhitespaceChar = config.get<string>('branchWhitespaceChar')!;
const branchValidationRegex = config.get<string>('branchValidationRegex')!;
const sanitize = (name: string) => name ?
name.trim().replace(/^-+/, '').replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar)
: name;
const rawBranchName = defaultName || await window.showInputBox({
placeHolder: localize('branch name', "Branch name"),
prompt: localize('provide branch name', "Please provide a new branch name"),
value: initialValue,
ignoreFocusOut: true,
validateInput: (name: string) => {
const validateName = new RegExp(branchValidationRegex);
const sanitizedName = sanitize(name);
if (validateName.test(sanitizedName)) {
// If the sanitized name that we will use is different than what is
// in the input box, show an info message to the user informing them
// the branch name that will be used.
return name === sanitizedName
? null
: {
message: localize('branch name does not match sanitized', "The new branch will be '{0}'", sanitizedName),
severity: InputBoxValidationSeverity.Info
};
}
let rawBranchName = defaultName;
return localize('branch name format invalid', "Branch name needs to match regex: {0}", branchValidationRegex);
if (!rawBranchName) {
// Branch name
if (!initialValue) {
const branchRandomNameEnabled = config.get<boolean>('branchRandomName.enable', false);
initialValue = `${branchPrefix}${branchRandomNameEnabled ? this.generateRandomBranchName(repository, branchWhitespaceChar) : ''}`;
}
});
// Branch name selection
const initialValueSelection: [number, number] | undefined =
initialValue.startsWith(branchPrefix) ? [branchPrefix.length, initialValue.length] : undefined;
rawBranchName = await window.showInputBox({
placeHolder: localize('branch name', "Branch name"),
prompt: localize('provide branch name', "Please provide a new branch name"),
value: initialValue,
valueSelection: initialValueSelection,
ignoreFocusOut: true,
validateInput: (name: string) => {
const validateName = new RegExp(branchValidationRegex);
const sanitizedName = sanitize(name);
if (validateName.test(sanitizedName)) {
// If the sanitized name that we will use is different than what is
// in the input box, show an info message to the user informing them
// the branch name that will be used.
return name === sanitizedName
? null
: {
message: localize('branch name does not match sanitized', "The new branch will be '{0}'", sanitizedName),
severity: InputBoxValidationSeverity.Info
};
}
return localize('branch name format invalid', "Branch name needs to match regex: {0}", branchValidationRegex);
}
});
}
return sanitize(rawBranchName || '');
}
private async _branch(repository: Repository, defaultName?: string, from = false): Promise<void> {
const branchName = await this.promptForBranchName(defaultName);
const branchName = await this.promptForBranchName(repository, defaultName);
if (!branchName) {
return;
@ -1871,7 +1929,7 @@ export class CommandCenter {
@command('git.renameBranch', { repository: true })
async renameBranch(repository: Repository): Promise<void> {
const currentBranchName = repository.HEAD && repository.HEAD.name;
const branchName = await this.promptForBranchName(undefined, currentBranchName);
const branchName = await this.promptForBranchName(repository, undefined, currentBranchName);
if (!branchName) {
return;

View file

@ -2,6 +2,11 @@
# yarn lockfile v1
"@joaomoreno/unique-names-generator@5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@joaomoreno/unique-names-generator/-/unique-names-generator-5.0.0.tgz#a67fe66e3d825c929fc97abfdf17fd80a72beab0"
integrity sha512-3kP6z7aoGEoM3tvhTBZioYa1QFkovOU8uxAlVclnZlXivwF/WTE5EcOzvoDdM+jtjJyWvCMDR1Q4RBjDqupD3A==
"@types/byline@4.2.31":
version "4.2.31"
resolved "https://registry.yarnpkg.com/@types/byline/-/byline-4.2.31.tgz#0e61fcb9c03e047d21c4496554c7116297ab60cd"