Git - improve heuristics for determining branch base (#193986)
* Add getBranchBaseFromReflog * Read/store branch merge base in the git config * Add getBranchBase() extension apipull/192260/head
parent
70a9d8f4ea
commit
8e80e950a4
|
@ -177,6 +177,10 @@ export class ApiRepository implements Repository {
|
|||
return this.repository.getBranches(query, cancellationToken);
|
||||
}
|
||||
|
||||
getBranchBase(name: string): Promise<Branch | undefined> {
|
||||
return this.repository.getBranchBase(name);
|
||||
}
|
||||
|
||||
setBranchUpstream(name: string, upstream: string): Promise<void> {
|
||||
return this.repository.setBranchUpstream(name, upstream);
|
||||
}
|
||||
|
|
|
@ -219,6 +219,7 @@ export interface Repository {
|
|||
deleteBranch(name: string, force?: boolean): Promise<void>;
|
||||
getBranch(name: string): Promise<Branch>;
|
||||
getBranches(query: BranchQuery, cancellationToken?: CancellationToken): Promise<Ref[]>;
|
||||
getBranchBase(name: string): Promise<Branch | undefined>;
|
||||
setBranchUpstream(name: string, upstream: string): Promise<void>;
|
||||
|
||||
getRefs(query: RefQuery, cancellationToken?: CancellationToken): Promise<Ref[]>;
|
||||
|
|
|
@ -1083,6 +1083,17 @@ export class Repository {
|
|||
return parseGitCommits(result.stdout);
|
||||
}
|
||||
|
||||
async reflog(ref: string, pattern: string): Promise<string[]> {
|
||||
const args = ['reflog', ref, `--grep-reflog=${pattern}`];
|
||||
const result = await this.exec(args);
|
||||
if (result.exitCode) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return result.stdout.split('\n')
|
||||
.filter(entry => !!entry);
|
||||
}
|
||||
|
||||
async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false): Promise<string> {
|
||||
const stdout = await this.buffer(object);
|
||||
|
||||
|
|
|
@ -1452,14 +1452,66 @@ export class Repository implements Disposable {
|
|||
|
||||
async getBranchBase(ref: string): Promise<Branch | undefined> {
|
||||
const branch = await this.getBranch(ref);
|
||||
const branchMergeBaseConfigKey = `branch.${branch.name}.vscode-merge-base`;
|
||||
|
||||
// Upstream
|
||||
if (branch.upstream) {
|
||||
return this.getBranch(`refs/remotes/${branch.upstream.remote}/${branch.upstream.name}`);
|
||||
return await this.getBranch(`refs/remotes/${branch.upstream.remote}/${branch.upstream.name}`);
|
||||
}
|
||||
|
||||
// Git config
|
||||
try {
|
||||
const mergeBase = await this.getConfig(branchMergeBaseConfigKey);
|
||||
if (mergeBase) {
|
||||
return await this.getBranch(mergeBase);
|
||||
}
|
||||
} catch (err) { }
|
||||
|
||||
// Reflog
|
||||
const branchFromReflog = await this.getBranchBaseFromReflog(ref);
|
||||
if (branchFromReflog) {
|
||||
await this.setConfig(branchMergeBaseConfigKey, branchFromReflog.name!);
|
||||
return branchFromReflog;
|
||||
}
|
||||
|
||||
// Default branch
|
||||
return await this.getDefaultBranch();
|
||||
const defaultBranch = await this.getDefaultBranch();
|
||||
if (defaultBranch) {
|
||||
await this.setConfig(branchMergeBaseConfigKey, defaultBranch.name!);
|
||||
return defaultBranch;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async getBranchBaseFromReflog(ref: string): Promise<Branch | undefined> {
|
||||
try {
|
||||
const reflogEntries = await this.repository.reflog(ref, 'branch: Created from *.');
|
||||
if (reflogEntries.length !== 1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Branch created from an explicit branch
|
||||
const match = reflogEntries[0].match(/branch: Created from (?<name>.*)$/);
|
||||
if (match && match.length === 2 && match[1] !== 'HEAD') {
|
||||
return await this.getBranch(match[1]);
|
||||
}
|
||||
|
||||
// Branch created from HEAD
|
||||
const headReflogEntries = await this.repository.reflog('HEAD', `checkout: moving from .* to ${ref.replace('refs/heads/', '')}`);
|
||||
if (headReflogEntries.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const match2 = headReflogEntries[headReflogEntries.length - 1].match(/checkout: moving from ([^\s]+)\s/);
|
||||
if (match2 && match2.length === 2) {
|
||||
return await this.getBranch(match2[1]);
|
||||
}
|
||||
|
||||
}
|
||||
catch (err) { }
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async getDefaultBranch(): Promise<Branch | undefined> {
|
||||
|
|
Loading…
Reference in New Issue