mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-01 22:12:26 +01:00
Git - get the diff and num stats for a commit (#284403)
This commit is contained in:
@@ -13,7 +13,7 @@ import { EventEmitter } from 'events';
|
||||
import * as filetype from 'file-type';
|
||||
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant, relativePathWithNoFallback, Mutable } from './util';
|
||||
import { CancellationError, CancellationToken, ConfigurationChangeEvent, LogOutputChannel, Progress, Uri, workspace } from 'vscode';
|
||||
import { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery as ApiRefQuery, InitOptions, Worktree } from './api/git';
|
||||
import { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery as ApiRefQuery, InitOptions, Worktree, DiffChange } from './api/git';
|
||||
import * as byline from 'byline';
|
||||
import { StringDecoder } from 'string_decoder';
|
||||
|
||||
@@ -1084,6 +1084,79 @@ function parseGitChanges(repositoryRoot: string, raw: string): Change[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseGitChangesRaw(repositoryRoot: string, raw: string): DiffChange[] {
|
||||
const changes: Change[] = [];
|
||||
const numStats = new Map<string, { insertions: number; deletions: number }>();
|
||||
|
||||
let index = 0;
|
||||
const segments = raw.trim().split('\x00').filter(s => s);
|
||||
|
||||
segmentsLoop:
|
||||
while (index < segments.length) {
|
||||
const segment = segments[index++];
|
||||
if (!segment) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (segment.startsWith(':')) {
|
||||
// Parse --raw output
|
||||
const [, , , , change] = segment.split(' ');
|
||||
const filePath = segments[index++];
|
||||
const originalUri = Uri.file(path.isAbsolute(filePath) ? filePath : path.join(repositoryRoot, filePath));
|
||||
|
||||
let uri = originalUri;
|
||||
let renameUri = originalUri;
|
||||
let status = Status.UNTRACKED;
|
||||
|
||||
switch (change[0]) {
|
||||
case 'A':
|
||||
status = Status.INDEX_ADDED;
|
||||
break;
|
||||
case 'M':
|
||||
status = Status.MODIFIED;
|
||||
break;
|
||||
case 'D':
|
||||
status = Status.DELETED;
|
||||
break;
|
||||
case 'R': {
|
||||
if (index >= segments.length) {
|
||||
break;
|
||||
}
|
||||
const newPath = segments[index++];
|
||||
if (!newPath) {
|
||||
break;
|
||||
}
|
||||
|
||||
status = Status.INDEX_RENAMED;
|
||||
uri = renameUri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(repositoryRoot, newPath));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown status
|
||||
break segmentsLoop;
|
||||
}
|
||||
|
||||
changes.push({ status, uri, originalUri, renameUri });
|
||||
} else {
|
||||
// Parse --numstat output
|
||||
const [insertions, deletions, filePath] = segment.split('\t');
|
||||
numStats.set(
|
||||
path.isAbsolute(filePath)
|
||||
? filePath
|
||||
: path.join(repositoryRoot, filePath), {
|
||||
insertions: insertions === '-' ? 0 : parseInt(insertions),
|
||||
deletions: deletions === '-' ? 0 : parseInt(deletions),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return changes.map(change => ({
|
||||
...change,
|
||||
insertions: numStats.get(change.uri.fsPath)?.insertions ?? 0,
|
||||
deletions: numStats.get(change.uri.fsPath)?.deletions ?? 0,
|
||||
}));
|
||||
}
|
||||
|
||||
export interface BlameInformation {
|
||||
readonly hash: string;
|
||||
readonly subject?: string;
|
||||
@@ -1694,8 +1767,24 @@ export class Repository {
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
async diffBetween2(ref1: string, ref2: string, options: { similarityThreshold?: number }): Promise<Change[]> {
|
||||
return await this.diffFiles(`${ref1}...${ref2}`, { cached: false, similarityThreshold: options.similarityThreshold });
|
||||
async diffBetweenWithStats(ref: string, options: { path?: string; similarityThreshold?: number }): Promise<DiffChange[]> {
|
||||
const args = ['diff', '--raw', '--numstat', '--diff-filter=ADMR', '-z',];
|
||||
|
||||
if (options.similarityThreshold) {
|
||||
args.push(`--find-renames=${options.similarityThreshold}%`);
|
||||
}
|
||||
|
||||
args.push(...[ref, '--']);
|
||||
if (options.path) {
|
||||
args.push(this.sanitizeRelativePath(options.path));
|
||||
}
|
||||
|
||||
const gitResult = await this.exec(args);
|
||||
if (gitResult.exitCode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return parseGitChangesRaw(this.repositoryRoot, gitResult.stdout);
|
||||
}
|
||||
|
||||
private async diffFiles(ref: string | undefined, options: { cached: boolean; similarityThreshold?: number }): Promise<Change[]> {
|
||||
@@ -1749,8 +1838,8 @@ export class Repository {
|
||||
}
|
||||
|
||||
|
||||
async diffTrees(treeish1: string, treeish2?: string, options?: { similarityThreshold?: number }): Promise<Change[]> {
|
||||
const args = ['diff-tree', '-r', '--name-status', '-z', '--diff-filter=ADMR'];
|
||||
async diffTrees(treeish1: string, treeish2?: string, options?: { similarityThreshold?: number }): Promise<DiffChange[]> {
|
||||
const args = ['diff-tree', '-r', '--raw', '--numstat', '--diff-filter=ADMR', '-z'];
|
||||
|
||||
if (options?.similarityThreshold) {
|
||||
args.push(`--find-renames=${options.similarityThreshold}%`);
|
||||
@@ -1769,7 +1858,7 @@ export class Repository {
|
||||
return [];
|
||||
}
|
||||
|
||||
return parseGitChanges(this.repositoryRoot, gitResult.stdout);
|
||||
return parseGitChangesRaw(this.repositoryRoot, gitResult.stdout);
|
||||
}
|
||||
|
||||
async getMergeBase(ref1: string, ref2: string, ...refs: string[]): Promise<string | undefined> {
|
||||
|
||||
Reference in New Issue
Block a user