Browse Source

Fixes #468 - better handles detached head state

main
Eric Amodio 6 years ago
parent
commit
457ab38974
14 changed files with 72 additions and 28 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +1
    -1
      src/commands/diffWithRevision.ts
  3. +1
    -1
      src/commands/openFileRevision.ts
  4. +1
    -1
      src/commands/showQuickFileHistory.ts
  5. +20
    -6
      src/git/git.ts
  6. +22
    -8
      src/git/models/branch.ts
  7. +6
    -1
      src/git/models/status.ts
  8. +4
    -0
      src/git/models/tag.ts
  9. +2
    -2
      src/gitService.ts
  10. +1
    -1
      src/views/explorerCommands.ts
  11. +3
    -3
      src/views/nodes/branchNode.ts
  12. +1
    -1
      src/views/nodes/branchesNode.ts
  13. +1
    -1
      src/views/nodes/remoteNode.ts
  14. +8
    -2
      src/views/nodes/statusNode.ts

+ 1
- 0
CHANGELOG.md View File

@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Fixed
- Fixes [#471](https://github.com/eamodio/vscode-gitlens/issues/471) - Don't use Ctrl+Alt+[character] as a shortcut
- Fixes [#478](https://github.com/eamodio/vscode-gitlens/issues/478) - `suppressShowKeyBindingsNotice` gets saved even when it is not required
- Fixes [#468](https://github.com/eamodio/vscode-gitlens/issues/468) - Commit history for detached HEAD doesn't work properly
## [8.5.4] - 2018-07-31
### Added

+ 1
- 1
src/commands/diffWithRevision.ts View File

@ -45,7 +45,7 @@ export class DiffWithRevisionCommand extends ActiveEditorCommand {
try {
const log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, {
maxCount: args.maxCount,
ref: (args.branchOrTag && args.branchOrTag.name) || gitUri.sha
ref: (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha
});
if (log === undefined) {
if (args.branchOrTag) {

+ 1
- 1
src/commands/openFileRevision.ts View File

@ -75,7 +75,7 @@ export class OpenFileRevisionCommand extends ActiveEditorCommand {
const log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, {
maxCount: args.maxCount,
ref: (args.branchOrTag && args.branchOrTag.name) || gitUri.sha
ref: (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha
});
if (log === undefined) {
if (args.branchOrTag) {

+ 1
- 1
src/commands/showQuickFileHistory.ts View File

@ -49,7 +49,7 @@ export class ShowQuickFileHistoryCommand extends ActiveEditorCachedCommand {
args.log = await Container.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, {
maxCount: args.maxCount,
range: args.range,
ref: (args.branchOrTag && args.branchOrTag.name) || gitUri.sha
ref: (args.branchOrTag && args.branchOrTag.ref) || gitUri.sha
});
if (args.log === undefined) {
if (args.branchOrTag) {

+ 20
- 6
src/git/git.ts View File

@ -603,33 +603,47 @@ export class Git {
}
}
static async revparse_currentBranch(repoPath: string): Promise<string | undefined> {
static async revparse_currentBranch(repoPath: string): Promise<[string, string?] | undefined> {
const params = ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@', '@{u}'];
const opts = { cwd: repoPath } as CommandOptions;
try {
const data = await gitCommandCore(opts, ...params);
return data;
return [data, undefined];
}
catch (ex) {
const msg = ex && ex.toString();
if (GitWarnings.headNotABranch.test(msg)) return undefined;
if (GitWarnings.headNotABranch.test(msg)) {
try {
const params = ['log', '-n1', '--format=%H', '--'];
const data = await gitCommandCore(opts, ...params);
if (data === undefined) return undefined;
// Matches output of `git branch -vv`
const sha = data.trim();
return [`(HEAD detached at ${this.shortenSha(sha)})`, sha];
}
catch {
return undefined;
}
}
const result = GitWarnings.noUpstream.exec(msg);
if (result !== null) return result[1];
if (result !== null) return [result[1], undefined];
if (GitWarnings.unknownRevision.test(msg)) {
try {
const params = ['symbolic-ref', '-q', '--short', 'HEAD'];
const data = await gitCommandCore(opts, ...params);
return data;
return [data, undefined];
}
catch {
return undefined;
}
}
return gitCommandDefaultErrorHandler(ex, opts, ...params);
gitCommandDefaultErrorHandler(ex, opts, ...params);
return undefined;
}
}

+ 22
- 8
src/git/models/branch.ts View File

@ -1,8 +1,10 @@
import { Git } from '../git';
import { GitStatus } from './status';
'use strict';
export class GitBranch {
readonly detached: boolean;
readonly name: string;
readonly remote: boolean;
readonly tracking?: string;
@ -18,7 +20,8 @@ export class GitBranch {
public readonly sha?: string,
tracking?: string,
ahead: number = 0,
behind: number = 0
behind: number = 0,
detached: boolean = false
) {
if (branch.startsWith('remotes/')) {
branch = branch.substring(8);
@ -28,7 +31,14 @@ export class GitBranch {
this.remote = false;
}
this.name = branch;
this.detached = detached || (this.current ? GitBranch.isDetached(branch) : false);
if (this.detached) {
this.name = GitBranch.formatDetached(this.sha!);
}
else {
this.name = branch;
}
this.tracking = tracking === '' || tracking == null ? undefined : tracking;
this.state = {
ahead: ahead,
@ -36,6 +46,10 @@ export class GitBranch {
};
}
get ref() {
return this.detached ? this.sha! : this.name;
}
private _basename: string | undefined;
getBasename(): string {
if (this._basename === undefined) {
@ -73,17 +87,17 @@ export class GitBranch {
return GitStatus.getUpstreamStatus(this.tracking, this.state, options);
}
isValid(): boolean {
return GitBranch.isValid(this.name);
}
static getRemote(branch: string): string {
return branch.substring(0, branch.indexOf('/'));
}
static isValid(name: string): boolean {
static formatDetached(sha: string): string {
return `(${Git.shortenSha(sha)}...)`;
}
static isDetached(name: string): boolean {
// If there is whitespace in the name assume this is not a valid branch name
// Deals with detached HEAD states
return name.match(/\s/) === null;
return name.match(/\s/) !== null || name.match(/\(detached\)/) !== null;
}
}

+ 6
- 1
src/git/models/status.ts View File

@ -4,6 +4,7 @@ import { Uri } from 'vscode';
import { GlyphChars } from '../../constants';
import { Strings } from '../../system';
import { GitUri } from '../gitUri';
import { GitBranch } from './branch';
import { GitLogCommit } from './logCommit';
export interface GitStatusUpstreamState {
@ -19,7 +20,11 @@ export class GitStatus {
public readonly files: GitStatusFile[],
public readonly state: GitStatusUpstreamState,
public readonly upstream?: string
) {}
) {
if (GitBranch.isDetached(branch)) {
this.branch = GitBranch.formatDetached(this.sha);
}
}
private _diff?: {
added: number;

+ 4
- 0
src/git/models/tag.ts View File

@ -7,6 +7,10 @@ export class GitTag {
public readonly annotation?: string
) {}
get ref() {
return this.name;
}
private _basename: string | undefined;
getBasename(): string {
if (this._basename === undefined) {

+ 2
- 2
src/gitService.ts View File

@ -891,8 +891,8 @@ export class GitService implements Disposable {
const data = await Git.revparse_currentBranch(repoPath);
if (data === undefined) return undefined;
const branch = data.split('\n');
return new GitBranch(repoPath, branch[0], true, undefined, branch[1]);
const branch = data[0].split('\n');
return new GitBranch(repoPath, branch[0], true, data[1], branch[1]);
}
async getBranches(repoPath: string | undefined): Promise<GitBranch[]> {

+ 1
- 1
src/views/explorerCommands.ts View File

@ -134,7 +134,7 @@ export class ExplorerCommands implements Disposable {
const branch = await Container.git.getBranch(node.repoPath);
if (branch === undefined) return;
const commonAncestor = await Container.git.getMergeBase(node.repoPath, branch.name, node.ref);
const commonAncestor = await Container.git.getMergeBase(node.repoPath, branch.ref, node.ref);
if (commonAncestor === undefined) return;
Container.resultsExplorer.showComparisonInResults(

+ 3
- 3
src/views/nodes/branchNode.ts View File

@ -28,7 +28,7 @@ export class BranchNode extends ExplorerRefNode {
const branchName = this.branch.getName();
if (this.explorer.config.branches.layout === ExplorerBranchesLayout.List) return branchName;
return GitBranch.isValid(branchName) && !this.current ? this.branch.getBasename() : branchName;
return this.current || GitBranch.isDetached(branchName) ? branchName : this.branch.getBasename();
}
get markCurrent(): boolean {
@ -36,11 +36,11 @@ export class BranchNode extends ExplorerRefNode {
}
get ref(): string {
return this.branch.name;
return this.branch.ref;
}
async getChildren(): Promise<ExplorerNode[]> {
const log = await Container.git.getLog(this.uri.repoPath!, { maxCount: this.maxCount, ref: this.branch.name });
const log = await Container.git.getLog(this.uri.repoPath!, { maxCount: this.maxCount, ref: this.ref });
if (log === undefined) return [new MessageNode('No commits yet')];
const branches = await Container.git.getBranches(this.uri.repoPath);

+ 1
- 1
src/views/nodes/branchesNode.ts View File

@ -40,7 +40,7 @@ export class BranchesNode extends ExplorerNode {
const hierarchy = Arrays.makeHierarchical(
branchNodes,
n => (n.branch.isValid() ? n.branch.getName().split('/') : [n.branch.name]),
n => (n.branch.detached ? [n.branch.name] : n.branch.getName().split('/')),
(...paths: string[]) => paths.join('/'),
this.explorer.config.files.compact
);

+ 1
- 1
src/views/nodes/remoteNode.ts View File

@ -40,7 +40,7 @@ export class RemoteNode extends ExplorerNode {
const hierarchy = Arrays.makeHierarchical(
branchNodes,
n => (n.branch.isValid() ? n.branch.getName().split('/') : [n.branch.name]),
n => (n.branch.detached ? [n.branch.name] : n.branch.getName().split('/')),
(...paths: string[]) => paths.join('/'),
this.explorer.config.files.compact
);

+ 8
- 2
src/views/nodes/statusNode.ts View File

@ -53,7 +53,8 @@ export class StatusNode extends ExplorerNode {
branch.sha,
branch.tracking,
status.state.ahead,
status.state.behind
status.state.behind,
branch.detached
);
}
@ -168,7 +169,12 @@ export class StatusBranchNode extends BranchNode {
async getTreeItem(): Promise<TreeItem> {
const item = await super.getTreeItem();
item.label = `History (${item.label})`;
if (item.label!.startsWith('(') && item.label!.endsWith(')')) {
item.label = `History ${item.label}`;
}
else {
item.label = `History (${item.label})`;
}
return item;
}
}

Loading…
Cancel
Save