Git - add `git.commitShortHashLength` setting (#237343)

lszomoru/command-palette-offset^2
Ladislau Szomoru 2025-01-06 17:05:37 +01:00 committed by GitHub
parent 4e2ba23aed
commit 3548eae0e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 53 additions and 30 deletions

View File

@ -3229,6 +3229,14 @@
"type": "string",
"default": "${authorName} (${authorDateAgo})",
"markdownDescription": "%config.blameStatusBarItem.template%"
},
"git.commitShortHashLength": {
"type": "number",
"default": 7,
"minimum": 7,
"maximum": 40,
"markdownDescription": "%config.commitShortHashLength%",
"scope": "resource"
}
}
},

View File

@ -278,9 +278,10 @@
"config.publishBeforeContinueOn.prompt": "Prompt to publish unpublished Git state when using Continue Working On from a Git repository",
"config.similarityThreshold": "Controls the threshold of the similarity index (the amount of additions/deletions compared to the file's size) for changes in a pair of added/deleted files to be considered a rename. **Note:** Requires Git version `2.18.0` or later.",
"config.blameEditorDecoration.enabled": "Controls whether to show blame information in the editor using editor decorations.",
"config.blameEditorDecoration.template": "Template for the blame information editor decoration. Supported variables:\n\n* `hash`: Commit hash\n\n* `hashShort`: First 8 characters of the commit hash\n\n* `subject`: First line of the commit message\n\n* `authorName`: Author name\n\n* `authorEmail`: Author email\n\n* `authorDate`: Author date\n\n* `authorDateAgo`: Time difference between now and the author date\n\n",
"config.blameEditorDecoration.template": "Template for the blame information editor decoration. Supported variables:\n\n* `hash`: Commit hash\n\n* `hashShort`: First N characters of the commit hash according to `#git.commitShortHashLength#`\n\n* `subject`: First line of the commit message\n\n* `authorName`: Author name\n\n* `authorEmail`: Author email\n\n* `authorDate`: Author date\n\n* `authorDateAgo`: Time difference between now and the author date\n\n",
"config.blameStatusBarItem.enabled": "Controls whether to show blame information in the status bar.",
"config.blameStatusBarItem.template": "Template for the blame information status bar item. Supported variables:\n\n* `hash`: Commit hash\n\n* `hashShort`: First 8 characters of the commit hash\n\n* `subject`: First line of the commit message\n\n* `authorName`: Author name\n\n* `authorEmail`: Author email\n\n* `authorDate`: Author date\n\n* `authorDateAgo`: Time difference between now and the author date\n\n",
"config.blameStatusBarItem.template": "Template for the blame information status bar item. Supported variables:\n\n* `hash`: Commit hash\n\n* `hashShort`: First N characters of the commit hash according to `#git.commitShortHashLength#`\n\n* `subject`: First line of the commit message\n\n* `authorName`: Author name\n\n* `authorEmail`: Author email\n\n* `authorDate`: Author date\n\n* `authorDateAgo`: Time difference between now and the author date\n\n",
"config.commitShortHashLength": "Controls the length of the commit short hash.",
"submenu.explorer": "Git",
"submenu.commit": "Commit",
"submenu.commit.amend": "Amend",

View File

@ -5,7 +5,7 @@
import { DecorationOptions, l10n, Position, Range, TextEditor, TextEditorChange, TextEditorDecorationType, TextEditorChangeKind, ThemeColor, Uri, window, workspace, EventEmitter, ConfigurationChangeEvent, StatusBarItem, StatusBarAlignment, Command, MarkdownString, languages, HoverProvider, CancellationToken, Hover, TextDocument } from 'vscode';
import { Model } from './model';
import { dispose, fromNow, IDisposable } from './util';
import { dispose, fromNow, getCommitShortHash, IDisposable } from './util';
import { Repository } from './repository';
import { throttle } from './decorators';
import { BlameInformation, Commit } from './git';
@ -186,14 +186,14 @@ export class GitBlameController {
this._onDidChangeConfiguration();
}
formatBlameInformationMessage(template: string, blameInformation: BlameInformation): string {
formatBlameInformationMessage(documentUri: Uri, template: string, blameInformation: BlameInformation): string {
const subject = blameInformation.subject && blameInformation.subject.length > this._subjectMaxLength
? `${blameInformation.subject.substring(0, this._subjectMaxLength)}\u2026`
: blameInformation.subject;
const templateTokens = {
hash: blameInformation.hash,
hashShort: blameInformation.hash.substring(0, 8),
hashShort: getCommitShortHash(documentUri, blameInformation.hash),
subject: emojify(subject ?? ''),
authorName: blameInformation.authorName ?? '',
authorEmail: blameInformation.authorEmail ?? '',
@ -260,7 +260,7 @@ export class GitBlameController {
markdownString.appendMarkdown(`\n\n---\n\n`);
}
markdownString.appendMarkdown(`[\`$(git-commit) ${blameInformationOrCommit.hash.substring(0, 8)} \`](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformationOrCommit.hash]))} "${l10n.t('View Commit')}")`);
markdownString.appendMarkdown(`[\`$(git-commit) ${getCommitShortHash(documentUri, blameInformationOrCommit.hash)} \`](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformationOrCommit.hash]))} "${l10n.t('View Commit')}")`);
markdownString.appendMarkdown(' ');
markdownString.appendMarkdown(`[$(copy)](command:git.blameStatusBarItem.copyContent?${encodeURIComponent(JSON.stringify(blameInformationOrCommit.hash))} "${l10n.t('Copy Commit Hash')}")`);
markdownString.appendMarkdown('  |  ');
@ -571,7 +571,9 @@ class GitBlameEditorDecoration implements HoverProvider {
}
private _onDidChangeConfiguration(e?: ConfigurationChangeEvent): void {
if (e && !e.affectsConfiguration('git.blame.editorDecoration.template')) {
if (e &&
!e.affectsConfiguration('git.commitShortHashLength') &&
!e.affectsConfiguration('git.blame.editorDecoration.template')) {
return;
}
@ -610,7 +612,7 @@ class GitBlameEditorDecoration implements HoverProvider {
const decorations = blameInformation.map(blame => {
const contentText = typeof blame.blameInformation !== 'string'
? this._controller.formatBlameInformationMessage(template, blame.blameInformation)
? this._controller.formatBlameInformationMessage(textEditor.document.uri, template, blame.blameInformation)
: blame.blameInformation;
return this._createDecoration(blame.lineNumber, contentText);
@ -663,7 +665,8 @@ class GitBlameStatusBarItem {
}
private _onDidChangeConfiguration(e: ConfigurationChangeEvent): void {
if (!e.affectsConfiguration('git.blame.statusBarItem.template')) {
if (!e.affectsConfiguration('git.commitShortHashLength') &&
!e.affectsConfiguration('git.blame.statusBarItem.template')) {
return;
}
@ -690,7 +693,7 @@ class GitBlameStatusBarItem {
const config = workspace.getConfiguration('git');
const template = config.get<string>('blame.statusBarItem.template', '${authorName} (${authorDateAgo})');
this._statusBarItem.text = `$(git-commit) ${this._controller.formatBlameInformationMessage(template, blameInformation[0].blameInformation)}`;
this._statusBarItem.text = `$(git-commit) ${this._controller.formatBlameInformationMessage(window.activeTextEditor.document.uri, template, blameInformation[0].blameInformation)}`;
this._statusBarItem.tooltip = this._controller.getBlameInformationHover(window.activeTextEditor.document.uri, blameInformation[0].blameInformation);
this._statusBarItem.command = {
title: l10n.t('View Commit'),

View File

@ -14,7 +14,7 @@ import { Model } from './model';
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getModifiedRange, getWorkingTreeAndIndexDiffInformation, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges } from './staging';
import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri';
import { dispose, grep, isDefined, isDescendant, pathEquals, relativePath, truncate } from './util';
import { dispose, getCommitShortHash, grep, isDefined, isDescendant, pathEquals, relativePath, truncate } from './util';
import { GitTimelineItem } from './timelineProvider';
import { ApiRepository } from './api/api1';
import { getRemoteSourceActions, pickRemoteSource } from './remoteSource';
@ -4286,16 +4286,17 @@ export class CommandCenter {
let title: string | undefined;
let historyItemParentId: string | undefined;
const rootUri = Uri.file(repository.root);
// If historyItem2 is not provided, we are viewing a single commit. If historyItem2 is
// provided, we are viewing a range and we have to include both start and end commits.
// TODO@lszomoru - handle the case when historyItem2 is the first commit in the repository
if (!historyItem2) {
const commit = await repository.getCommit(historyItem1.id);
title = `${historyItem1.id.substring(0, 8)} - ${truncate(commit.message)}`;
title = `${getCommitShortHash(rootUri, historyItem1.id)} - ${truncate(commit.message)}`;
historyItemParentId = historyItem1.parentIds.length > 0 ? historyItem1.parentIds[0] : `${historyItem1.id}^`;
} else {
title = l10n.t('All Changes ({0} ↔ {1})', historyItem2.id.substring(0, 8), historyItem1.id.substring(0, 8));
title = l10n.t('All Changes ({0} ↔ {1})', getCommitShortHash(rootUri, historyItem2.id), getCommitShortHash(rootUri, historyItem1.id));
historyItemParentId = historyItem2.parentIds.length > 0 ? historyItem2.parentIds[0] : `${historyItem2.id}^`;
}
@ -4310,8 +4311,9 @@ export class CommandCenter {
return;
}
const modifiedShortRef = historyItem.id.substring(0, 8);
const originalShortRef = historyItem.parentIds.length > 0 ? historyItem.parentIds[0].substring(0, 8) : `${modifiedShortRef}^`;
const rootUri = Uri.file(repository.root);
const modifiedShortRef = getCommitShortHash(rootUri, historyItem.id);
const originalShortRef = historyItem.parentIds.length > 0 ? getCommitShortHash(rootUri, historyItem.parentIds[0]) : `${modifiedShortRef}^`;
const title = l10n.t('All Changes ({0} ↔ {1})', originalShortRef, modifiedShortRef);
const multiDiffSourceUri = toGitUri(Uri.file(repository.root), historyItem.id, { scheme: 'git-changes' });
@ -4350,8 +4352,9 @@ export class CommandCenter {
return;
}
const rootUri = Uri.file(repository.root);
const commit = await repository.getCommit(historyItemId);
const title = `${historyItemId.substring(0, 8)} - ${truncate(commit.message)}`;
const title = `${getCommitShortHash(rootUri, historyItemId)} - ${truncate(commit.message)}`;
const historyItemParentId = commit.parents.length > 0 ? commit.parents[0] : `${historyItemId}^`;
const multiDiffSourceUri = Uri.from({ scheme: 'scm-history-item', path: `${repository.root}/${historyItemParentId}..${historyItemId}` });

View File

@ -6,20 +6,22 @@
import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryItemRef, l10n, SourceControlHistoryItemRefsChangeEvent } from 'vscode';
import { Repository, Resource } from './repository';
import { IDisposable, deltaHistoryItemRefs, dispose, filterEvent } from './util';
import { IDisposable, deltaHistoryItemRefs, dispose, filterEvent, getCommitShortHash } from './util';
import { toGitUri } from './uri';
import { Branch, LogOptions, Ref, RefType } from './api/git';
import { emojify, ensureEmojis } from './emoji';
import { Commit } from './git';
import { OperationKind, OperationResult } from './operation';
function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef {
function toSourceControlHistoryItemRef(repository: Repository, ref: Ref): SourceControlHistoryItemRef {
const rootUri = Uri.file(repository.root);
switch (ref.type) {
case RefType.RemoteHead:
return {
id: `refs/remotes/${ref.name}`,
name: ref.name ?? '',
description: ref.commit ? l10n.t('Remote branch at {0}', ref.commit.substring(0, 8)) : undefined,
description: ref.commit ? l10n.t('Remote branch at {0}', getCommitShortHash(rootUri, ref.commit)) : undefined,
revision: ref.commit,
icon: new ThemeIcon('cloud'),
category: l10n.t('remote branches')
@ -28,7 +30,7 @@ function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef {
return {
id: `refs/tags/${ref.name}`,
name: ref.name ?? '',
description: ref.commit ? l10n.t('Tag at {0}', ref.commit.substring(0, 8)) : undefined,
description: ref.commit ? l10n.t('Tag at {0}', getCommitShortHash(rootUri, ref.commit)) : undefined,
revision: ref.commit,
icon: new ThemeIcon('tag'),
category: l10n.t('tags')
@ -37,7 +39,7 @@ function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef {
return {
id: `refs/heads/${ref.name}`,
name: ref.name ?? '',
description: ref.commit ? ref.commit.substring(0, 8) : undefined,
description: ref.commit ? getCommitShortHash(rootUri, ref.commit) : undefined,
revision: ref.commit,
icon: new ThemeIcon('git-branch'),
category: l10n.t('branches')
@ -178,7 +180,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
// Refs (alphabetically)
const historyItemRefs = this.repository.refs
.map(ref => toSourceControlHistoryItemRef(ref))
.map(ref => toSourceControlHistoryItemRef(this.repository, ref))
.sort((a, b) => a.id.localeCompare(b.id));
// Auto-fetch
@ -207,13 +209,13 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
for (const ref of refs) {
switch (ref.type) {
case RefType.RemoteHead:
remoteBranches.push(toSourceControlHistoryItemRef(ref));
remoteBranches.push(toSourceControlHistoryItemRef(this.repository, ref));
break;
case RefType.Tag:
tags.push(toSourceControlHistoryItemRef(ref));
tags.push(toSourceControlHistoryItemRef(this.repository, ref));
break;
default:
branches.push(toSourceControlHistoryItemRef(ref));
branches.push(toSourceControlHistoryItemRef(this.repository, ref));
break;
}
}
@ -259,7 +261,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
message: emojify(commit.message),
author: commit.authorName,
icon: new ThemeIcon('git-commit'),
displayId: commit.hash.substring(0, 8),
displayId: getCommitShortHash(Uri.file(this.repository.root), commit.hash),
timestamp: commit.authorDate?.getTime(),
statistics: commit.shortStat ?? { files: 0, insertions: 0, deletions: 0 },
references: references.length !== 0 ? references : undefined

View File

@ -23,7 +23,7 @@ import { IPushErrorHandlerRegistry } from './pushError';
import { IRemoteSourcePublisherRegistry } from './remotePublisher';
import { StatusBarCommands } from './statusbar';
import { toGitUri } from './uri';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util';
import { IFileWatcher, watch } from './watch';
import { detectEncoding } from './encoding';
@ -1657,7 +1657,7 @@ export class Repository implements Disposable {
}
async checkout(treeish: string, opts?: { detached?: boolean; pullBeforeCheckout?: boolean }): Promise<void> {
const refLabel = opts?.detached ? treeish.substring(0, 8) : treeish;
const refLabel = opts?.detached ? getCommitShortHash(Uri.file(this.root), treeish) : treeish;
await this.run(Operation.Checkout(refLabel),
async () => {
@ -1675,7 +1675,7 @@ export class Repository implements Disposable {
}
async checkoutTracking(treeish: string, opts: { detached?: boolean } = {}): Promise<void> {
const refLabel = opts.detached ? treeish.substring(0, 8) : treeish;
const refLabel = opts.detached ? getCommitShortHash(Uri.file(this.root), treeish) : treeish;
await this.run(Operation.CheckoutTracking(refLabel), () => this.repository.checkout(treeish, [], { ...opts, track: true }));
}

View File

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef, l10n } from 'vscode';
import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef, l10n, workspace, Uri } from 'vscode';
import { dirname, sep, relative } from 'path';
import { Readable } from 'stream';
import { promises as fs, createReadStream } from 'fs';
@ -766,3 +766,9 @@ export function fromNow(date: number | Date, appendAgoLabel?: boolean, useFullTi
}
}
}
export function getCommitShortHash(scope: Uri, hash: string): string {
const config = workspace.getConfiguration('git', scope);
const shortHashLength = config.get<number>('commitShortHashLength', 7);
return hash.substring(0, shortHashLength);
}