mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
Git - branch prefix + random name generation (#149069)
This commit is contained in:
parent
e5b5dc2417
commit
b44e3caa96
|
@ -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",
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue